1. 서버 코드 분석
1-1. 소켓 초기화 및 설정
SocketUtils::Init();
- 설명:
- 네트워크 소켓 라이브러리를 초기화하는 함수입니다.
- Windows에서는
WSAStartup()을 호출하여 소켓을 사용할 수 있도록 준비합니다.
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
return 0;
- 설명:
socket() 함수는 소켓을 생성합니다.
- 매개변수:
AF_INET: IPv4 인터넷 프로토콜을 사용.
SOCK_STREAM: TCP 프로토콜 기반의 스트림 소켓.
0: 기본 프로토콜(TCP)을 사용.
- 반환값:
- 성공 시 소켓 핸들을 반환.
- 실패 시
INVALID_SOCKET 반환.
- 오류 처리:
u_long on = 1;
if (::ioctlsocket(listenSocket, FIONBIO, &on) == INVALID_SOCKET)
return 0;
- 설명:
ioctlsocket 함수는 소켓의 속성을 설정합니다.
FIONBIO 플래그를 사용하면 소켓이 논블로킹 모드로 설정됩니다.
- on = 1: 논블로킹 모드 활성화.
- 반환값:
- 성공 시
0 반환.
- 실패 시
INVALID_SOCKET 반환.
SocketUtils::SetReuseAddress(listenSocket, true);
- 설명:
- 소켓 옵션 중 하나인 주소 재사용(Reuse Address)를 설정합니다.
- 이전에 사용된 소켓 주소가 바로 재사용될 수 있도록 합니다.
if (SocketUtils::BindAnyAddress(listenSocket, 7777) == false)
return 0;
- 설명:
bind() 함수를 사용해 소켓을 특정 주소와 포트에 바인딩합니다.
- 7777: 서버가 수신할 포트 번호입니다.
- 반환값:
- 성공 시
true.
- 실패 시 프로그램을 종료.
if (SocketUtils::Listen(listenSocket, SOMAXCONN) == false)
return 0;
- 설명:
listen() 함수는 소켓을 수신 대기 상태로 설정합니다.
- SOMAXCONN: 최대 대기 가능한 연결 요청 수를 설정.
1-2. 클라이언트 연결 처리
SOCKADDR_IN clientAddr;
int32 addrLen = sizeof(clientAddr);
- 설명:
- 클라이언트 주소 정보를 저장할
SOCKADDR_IN 구조체를 선언합니다.
addrLen은 클라이언트 주소 구조체의 크기를 설정합니다.
while (true)
{
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
}
cout << "Client Connected!" << endl;
- 설명:
accept() 함수:
- 클라이언트 연결 요청을 수락합니다.
- 논블로킹 모드에서는 클라이언트가 없을 경우
INVALID_SOCKET을 반환합니다.
WSAGetLastError():
- 오류 코드가
WSAEWOULDBLOCK이면, 클라이언트가 없음을 의미합니다.
- 계속 루프를 실행합니다.
- 연결이 성공하면
"Client Connected!" 메시지를 출력합니다.
1-3. 데이터 수신 처리
while (true)
{
char recvBuffer[1000];
int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
if (recvLen == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
break;
}
cout << "Recv Data = " << recvBuffer << endl;
cout << "Recv Data len = " << recvLen << endl;
}
- 설명:
recv() 함수:
- 데이터를 수신합니다.
- 논블로킹 모드에서 수신할 데이터가 없으면
SOCKET_ERROR와 함께 WSAEWOULDBLOCK 오류 코드가 반환됩니다.
- 오류 처리:
WSAEWOULDBLOCK: 데이터가 아직 도착하지 않음을 의미하며, 계속 루프를 실행합니다.
- 다른 오류 코드가 발생하면 루프를 종료합니다.
- 데이터 수신 성공 시:
SocketUtils::Close(listenSocket);
- 설명:
close() 함수로 소켓을 닫고 네트워크 리소스를 해제합니다.
2. 클라이언트 코드 분석
2-1. 소켓 생성 및 설정
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
return 0;
u_long on = 1;
if (::ioctlsocket(clientSocket, FIONBIO, &on) == INVALID_SOCKET)
return 0;
- 설명:
socket() 함수:
ioctlsocket():
2-2. 서버에 연결 요청
SOCKADDR_IN serverAddr;
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777);
while (true)
{
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
if (::WSAGetLastError() == WSAEISCONN)
break;
}
}
- 설명:
connect() 함수:
- 서버에 연결을 시도합니다.
- 논블로킹 모드에서는
WSAEWOULDBLOCK 오류가 반환되면 연결 시도가 진행 중임을 의미합니다.
WSAEISCONN:
2-3. 데이터 송신
while (true)
{
char sendBuffer[100] = "Hello I am Client!";
int32 sendLen = sizeof(sendBuffer);
if (::send(clientSocket, sendBuffer, sendLen, 0) == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
cout << "Send Data ! Len = " << sendLen << endl;
}
this_thread::sleep_for(1s);
}
- 설명:
send() 함수:
- 데이터를 서버에 송신합니다.
- 논블로킹 모드에서 버퍼가 가득 차 있으면
WSAEWOULDBLOCK 오류를 반환합니다.
- 송신 성공:
- 1초 대기:
sleep_for(1s)를 사용해 1초마다 데이터를 송신합니다.
2-4. 소켓 종료
SocketUtils::Close(clientSocket);
- 설명:
- 소켓을 닫아 클라이언트의 네트워크 리소스를 해제합니다.
3. 요약 및 흐름
서버:
- 소켓 생성 → 논블로킹 모드 설정 → 바인딩 → 수신 대기 → 클라이언트 연결 → 데이터 수신.
클라이언트:
- 소켓 생성 → 논블로킹 모드 설정 → 서버 연결 → 데이터 송신.