1. Blocking Socket
1.1 Blocking
- accept, connect, recv, send
- 대부분 소켓 관련 함수들은 호출 스레드를 블로킹 상태로 만듦
- 블로킹 상태: 현재 스레드가 더 이상 코드를 진행하지 않고 block 상태로 멈춰 있는 상태
1.2 Blocking 상태가 안 좋은 이유
- 서버 1개와 연결된 클라이언트 수는 10개
- 서버는 모든 클라이언트 패킷을 번갈아 가면서 받아야 함
- 그러나 하나의 클라이언트에서 보내기만 하고 클라이언트가 종료가 된다면 서버는 블로킹 상태 유지
1.3 Blocking 상태를 사용하지 않는 방법
- 함수 호출에 필요한 소켓을 논블로킹 상태로 변환
- 이 소켓이 호출되고 클라이언트와 소통이 없으면 에러 코드를 반환 및 다음 클라이언트로 넘어감
2. Non-blocking Socket
2.1 Non-blocking Server
- 기존 TCP서버를 이용하여 수정
- https://choiprogramming.tistory.com/101
[SEVER] TCP Server
1. TCP 1.1 TCP의 의미 - 인터넷상에서 데이터를 메세지의 형태로 보내기 위해 IP를 함께 사용하는 프로토콜 1.2 특징 - 연결 지향 방식 - 3-way handshaking과정을 통해 연결 설정 및 4-way handshaking을 통해
choiprogramming.tistory.com
2.2 Non-blocking Socket
- 소켓을 논블로킹 상태로 만들기
- ioctlsocket 함수 사용 (Input / Output ConTroL Socket)
- int ioctlsocket(SOCKET sock, long cmd, u_long* argp)
- sock: 논블로킹 모드로 설정할 소켓을 의미
- cmd: 소켓에 대해 수행할 동작을 의미하는 커맨드
- FIONBIO
- argp: cmd가 수행할 동작에 대한 판별 값
- 0: 블로킹 상태
- 0이 아닌 다른 값: 논 블로킹 상태
int main()
{
// Init WSA
// Socket Create
// Server IP/HOST
u_long NonBlockingMode = 1;
if (::ioctlsocket(listenSocket, FIONBIO, &NonBlockingMode) == INVALID_SOCKET) return 0;
// BIND
// LISTEN
}
2.3 Accept
- 논블로킹 소켓을 만든 상태
- 클라이언트가 연결할 때까지 대기를 하고 있지만 논블로킹 상태이므로 멈춰있지 않음
- 클라이언트의 접속이 없지만 블로킹을 해야하는데 논블로킹 상태
- WSAEWOULDBLOCK: 연결요청을 받지 못했으므로 처음부터 다시하라는 의미
- 오류는 아니므로 서버가 종료되지 않음
int main()
{
// Init WSA
// Socket Create
// Server IP/HOST
// Non-Blocking Socket
// BIND
// LISTEN
while (true)
{
SOCKADDR_IN clientAddr;
::memset(&serverAddr, 0, sizeof(serverAddr));
int32 addrLength = sizeof(clientAddr);
SOCKET clientSocket = ::accept(listenSocket,
reinterpret_cast<SOCKADDR*>(&clientAddr),
&addrLength);
if (clientSocket == INVALID_SOCKET)
{
// 접속 요청을 한 소켓이 없음
if (::WSAGetLastError() == WSAEWOULDBLOCK) continue; // 문제가 되는 상황X
}
}
// Close WSA
}
2.4 Recv & Send
- Accept와 마찬가지로 논블로킹 상태
- recv: 클라이언트가 보낸 값이 없음 (데이터를 아직 못받음)
- send: 전송할 버퍼 공간이 없음 (데이터 초과?)
int main()
{
// Init WSA
// Socket Create
// Server IP/HOST
// Non-Blocking Socket
// BIND
// LISTEN
while (true)
{
// 클라와 서버의 접속 완료
while (true)
{
char recvBuffer[MAX_BUFFER];
int32 recvLength = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
if (recvLength == INVALID_SOCKET)
{
// 클라이언트가 보낸 값이 없음 - 데이터를 아직 못받음
if (::WSAGetLastError() == WSAEWOULDBLOCK) continue; // 문제가 되는 상황X
// TODO - 다른 에러 확인
}
cout << recvBuffer << endl;
}
}
// Close WSA
}
3. Non-blocking Client
int main()
{
// WSA 생성
WSADATA Init;
if (::WSAStartup(MAKEWORD(2, 2), &Init) != 0) return 0;
// 소켓 생성
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) return 0;
// 서버 주소 및 포트 생성
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);
// 논블로킹 소켓 생성
u_long NonBlockingMode = 1;
if (::ioctlsocket(clientSocket, FIONBIO, &NonBlockingMode) == INVALID_SOCKET) return 0;
// 서버와의 통신
while (true)
{
// 서버와의 연결
if (::connect(clientSocket, reinterpret_cast<SOCKADDR*>(&serverAddr), sizeof(serverAddr)) == SOCKET_ERROR)
{
// 연결할 서버가 없음
if (::WSAGetLastError() == WSAEWOULDBLOCK) continue; // 문제가 되는 상황X
if (::WSAGetLastError() == WSAEISCONN) break; // 이미 연결된 상태
}
}
// 서버 통신
while (true)
{
char sendBuffer[MAX_BUFFER] = "TEST SERVER";
// 서버에게 메세지를 보냄
int32 sendLength = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
if (sendLength <= SOCKET_ERROR)
{
// 공간이 부족하거나 보낼 수 없는 경우
if (::WSAGetLastError() == WSAEWOULDBLOCK) continue; // 문제가 되는 상황X
}
this_thread::sleep_for(1s);
}
}
'Create Game > [Window API] Game Client & Game Server' 카테고리의 다른 글
[SERVER] WSAEvent Select (0) | 2023.01.13 |
---|---|
[SERVER] Select (1) | 2023.01.12 |
[SEVER] UDP Server (0) | 2023.01.10 |
[SEVER] TCP Server (0) | 2023.01.10 |
[SERVER] Memory Order (0) | 2023.01.05 |