포스팅 계기
네트워크 프로그래밍을 하다 보면, 소켓 통신에서 예상치 못한 오류가 발생하는 경우가 많다. 특히, 논블로킹 소켓을 사용할 때 자주 마주치는 WSAEWOULDBLOCK(10035)와 WSAEALREADY(10037) 오류는 많은 개발자들에게 혼란을 준다.
최근 현업에서 TCP 클라이언트-서버 환경을 구축하는 과정에서 이 오류를 해결해야 하는 상황이 발생했다. 여기에 더해, 와이어샤크(Wireshark) 패킷 분석을 통해 서버 측에서 RST (Reset) 패킷이 발생하는 문제도 발견하였다. 그래서 해당 포스팅을 작성하게 되었다. 이번 포스팅에서는 WSAEWOULDBLOCK(10035) 및 WSAEALREADY(10037) 오류의 개념과 해결 방법을 정리하고, RST 패킷이 발생하는 원인과 해결책까지 설명해 보겠다. 네트워크 프로그래밍을 하는 분들에게 도움이 되었으면 한다.
WSAEWOULDBLOCK(10035)와 WSAEALREADY(10037) 오류 개념
- WSAEWOULDBLOCK (10035)란?
- 논블로킹 모드의 소켓에서 send() 또는 recv()를 호출할 때, 소켓이 아직 준비되지 않았을 경우 발생하는 오류다.
- 즉, 데이터를 보낼 준비가 안 되었거나 받을 데이터가 아직 없을 때 발생하는 오류다.
- 해결 방법은 select() 또는 WSAPoll()을 사용하여 소켓이 준비될 때까지 대기하는 것이다.
- WSAEALREADY (10037)란?
- 논블로킹 모드에서 connect()를 호출했을 때, 이미 연결이 진행 중인 상태에서 다시 connect()를 호출하면 발생하는 오류다.
- WSAEWOULDBLOCK과 비슷하지만, 이미 connect()가 실행 중인 상태에서 추가 호출이 발생할 때 나타난다.
- 해결 방법은 select()를 사용하여 연결 완료 여부를 확인한 후, getsockopt(SO_ERROR)를 호출하여 연결 상태를 점검하는 것이다.
해결 방법 (예제 코드 포함)
- WSAEWOULDBLOCK(10035) 해결 방법
아래 코드는 select()를 사용하여 소켓이 데이터 송수신이 가능할 때까지 대기하는 예제다.
d_set writeSet;
FD_ZERO(&writeSet);
FD_SET(socket, &writeSet);
timeval timeout;
timeout.tv_sec = 5; // Close하기 까지의 대기 시간을 기본적으로 5초로 설정
timeout.tv_usec = 0;
int result = select(0, NULL, &writeSet, NULL, &timeout);
if (result > 0 && FD_ISSET(socket, &writeSet)) {
send(socket, buffer, length, 0);
} else {
printf("Socket is not ready for writing. WSAEWOULDBLOCK occurred.\n");
}
- WSAEALREADY(10037) 해결 방법
아래 코드는 connect() 호출 후 select()를 사용하여 연결 상태를 확인하는 방법이다.
int result = connect(socket, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (result == SOCKET_ERROR) {
int error = WSAGetLastError();
if (error == WSAEWOULDBLOCK || error == WSAEALREADY) {
fd_set writeSet;
FD_ZERO(&writeSet);
FD_SET(socket, &writeSet);
timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
result = select(0, NULL, &writeSet, NULL, &timeout);
if (result > 0) {
int so_error;
socklen_t len = sizeof(so_error);
getsockopt(socket, SOL_SOCKET, SO_ERROR, reinterpret_cast<int*>(&so_error), &len);
if (so_error == 0) {
printf("Connection successful!\n");
} else {
printf("Connection failed with error: %d\n", so_error);
}
}
}
}
RST 패킷이란? (Reset 패킷)
- RST 패킷이란?
- RST (Reset) 패킷은 TCP 연결이 비정상적으로 종료될 때 발생한다. 주로 아래와 같은 경우에 나타난다:
- 서버가 accept() 후 즉시 closesocket()을 호출하는 경우
- 클라이언트가 연결을 유지하려 하는데 서버가 closesocket()을 호출한 경우
- 서버의 방화벽이 특정 시간 동안 연결을 유지하지 못하도록 설정된 경우
- 서버가 SO_LINGER 옵션을 설정하여 소켓을 즉시 종료하는 경우
- RST (Reset) 패킷은 TCP 연결이 비정상적으로 종료될 때 발생한다. 주로 아래와 같은 경우에 나타난다:
RST 패킷 문제 해결 방법
- accept() 후 일정 시간 대기
SOCKET clientSock = accept(serverSock, NULL, NULL);
if (clientSock != INVALID_SOCKET) {
printf("Client connected!\n");
Sleep(5000); // 일정 시간 대기
closesocket(clientSock);
}
- SO_LINGER 비활성화
linger so_linger;
so_linger.l_onoff = 0;
so_linger.l_linger = 0;
setsockopt(serverSock, SOL_SOCKET, SO_LINGER, (char*)&so_linger, sizeof(so_linger));
포스팅 마무리
이번 글에서는 WSAEWOULDBLOCK(10035), WSAEALREADY(10037) 오류와 RST 패킷 발생 원인 및 해결 방법을 정리해 보았다. 이 오류들은 실제 네트워크 프로그래밍에서 흔히 발생하는 문제이며, 문제를 정확히 이해하고 적절한 방법으로 처리하면 쉽게 해결할 수 있다.
이번 포스팅이 현업에서 같은 문제를 겪고 있는 개발자들에게 도움이 되었으면 좋겠다. 네트워크 프로그래밍은 쉽지 않지만, 하나씩 해결해 나가면 점점 익숙해질 것이라고 기대한다.
'C++ 개발이야기 > Socket 통신' 카테고리의 다른 글
고성능 네트워크 프로그래밍 기법 – 실무에서 반드시 알아야 할 최적화 기술 (0) | 2025.02.28 |
---|---|
보안과 암호화된 소켓 통신 - C++ (0) | 2025.02.27 |
비동기 소켓 프로그래밍 - C++ (0) | 2025.02.26 |
멀티스레드 기반 소켓 프로그래밍 : 서버 성능 극대화를 위한 필수 개념 - c++ (0) | 2025.02.26 |
C++에서 TCP/UDP 소켓 프로그래밍 입문 (0) | 2025.02.25 |