1. WSAEventSelect (Windows Socket Async)
1.1 WSAEventSelect 의미
- 입출력 함수를 안전하게 호출할 수 있는 시점을 운영체제가 알려줌
- 단순한 입출력 방식보다 편리하게 여러개의 소켓을 처리
- 운영체제에서 함수 호출의 시점을 알려주기 때문에 비동기처럼 처리할 수 있음
- 비동기 입출력 모델 (Overlapped I/O, IOCP)
1.2 Select Code 수정
https://choiprogramming.tistory.com/110
[SERVER] Select
1. Select 1.1 Non-blocking Socket의 문제 - 함수의 결과를 예상X - 패킷 수신 여부와 관계없이 바로 리턴 → buf의 상태가 어떤지 알 방법X - 소켓이 Read/Write 할 수 있는 상태인지 아닌지 판별해 주는 select
choiprogramming.tistory.com
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 클라이언트로 전달 받은 소켓 저장할 배열 생성
// Close WSA
}
1.3 WSAEventSelect
- 이벤트 객체를 활용하여 네트워크의 이벤트 상태를 감지
- 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성 (1 대 1 대응상태)
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
vector<Session> socketVector;
vector<WSAEVENT> socketEventVector;
// Close WSA
}
- 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
- Listen Socket은 3번째 인자로 선택한 이벤트를 감지할 수 있음
- 3번째 인자는 이벤트가 감지할 이벤트의 종류를 지정
- FD (ACCPET, READ, WRITE, OOB, QOS, CLOSE)
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
// 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
WSAEVENT listenEvent = WSACreateEvent();
socketVector.push_back(Session{listenSocket});
socketEventVector.push_back(listenEvent);
WSAEventSelect(listenSocket, listenEvent, FD_ACCEPT | FD_CLOSE);
// Close WSA
}
- 발생한 이벤트를 감시
DWORD WSAWaitForMultipleEvents(DWORD cEvents,const WSAEVENT *lphEvents,
WINBOOL fWaitAll,DWORD dwTimeout,WINBOOL fAlertable);
- 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지
- cEvents: 대기할 이벤트의 개수
- &lphEvent: 대기할 이벤트 배열
- fWaitAll: TRUE이면 모든 객체가 시그널 상태가 되기 전까지 대기
- dwTimeout: 이벤트 객체 대기상태 대기 시간
- fAlertable: 스레드 실행에 대한 입출력 완료 루틴 설정
- 발생할 이벤트의 크기는 STL을 사용해 사이즈를 알 수 있고 이벤트 또한 알 수 있음
- 하나의 이벤트만 확인하므로 FALSE 및 무한정으로 대기
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
// 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
while (true)
{
// 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지
DWORD Index = WSAWaitForMultipleEvents(socketEventVector.size(), &socketEventVector[0], FALSE, WSA_INFINITE, FALSE);
if (Index == WSA_WAIT_FAILED) continue;
// 몇 번째 인덱스에서 이벤트가 발생했는지 알 수 있음
Index -= WSA_WAIT_EVENT_0;
}
// Close WSA
}
- 최종적으로 구한 인덱스를 통해 발생한 이벤트의 종류를 알아냄
int WSAEnumNetworkEvents(SOCKET s,WSAEVENT hEventObject,LPWSANETWORKEVENTS lpNetworkEvents);
- 발생한 이벤트 종류 기록을 채워넣을 구조체 (발생한 에러 또한 담길 수 있음)
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
// 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
while (true)
{
// 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지 및 이벤트 발생 확인
// 발생한 이벤트 종류 기록을 채워넣을 구조체 (발생한 에러 또한 담길 수 있음)
WSANETWORKEVENTS wsaNetEvents;
if (WSAEnumNetworkEvents(socketVector[Index].socket, socketEventVector[Index], &wsaNetEvents) == SOCKET_ERROR) continue;
}
// Close WSA
}
- 클라이언트 접속 이벤트 발생 (FD_ACCEPT 이벤트 처리)
- 접속이 성공하면 클라이언트에 맞는 이벤트 생성 및 클라이언트 소켓과 묶음
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
// 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
while (true)
{
// 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지 및 이벤트 발생 확인
// 발생한 이벤트 종류 기록을 채워넣을 구조체 (발생한 에러 또한 담길 수 있음)
// 클라이언트 접속 이벤트 발생 - FD_ACCEPT 이벤트 처리
if (wsaNetEvents.lNetworkEvents & FD_ACCEPT)
{
if (wsaNetEvents.iErrorCode[FD_ACCEPT_BIT] != 0) continue;
// 접속 성공
SOCKADDR_IN clientAddr;
int32 addrLength = sizeof(clientAddr);
SOCKET clientSocket = ::accept(socketVector[Index].socket,
reinterpret_cast<SOCKADDR*>(&clientAddr), &addrLength);
if (clientSocket == INVALID_SOCKET) continue;
// 이벤트 생성 및 클라이언트 소켓과 이벤트를 묶음
WSAEVENT clientEvent = WSACreateEvent();
socketVector.push_back(Session{ clientSocket });
socketEventVector.push_back(clientEvent);
WSAEventSelect(clientSocket, clientEvent, FD_READ | FD_WRITE |FD_CLOSE);
}
}
// Close WSA
}
- READ 이벤트 검사
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
// 이벤트 상태를 감지할 수 있는 이벤트 배열 및 소켓 배열 생성
// 생성한 Listen Socket 및 Event 를 묶고 벡터에 저장
while (true)
{
// 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지 및 이벤트 발생 확인
// 발생한 이벤트 종류 기록을 채워넣을 구조체 (발생한 에러 또한 담길 수 있음)
// 클라이언트 접속 이벤트 발생 - FD_ACCEPT 이벤트 처리
// READ 이벤트 검사
if (wsaNetEvents.lNetworkEvents & FD_READ)
{
if (wsaNetEvents.iErrorCode[FD_READ_BIT] != 0) continue;
// 서버에게 메세지를 보냄
int32 recvLength = ::recv(socketVector[Index].socket, socketVector[Index].recvBuffer, MAX_BUFFER, 0);
if (recvLength <= SOCKET_ERROR)
if (::WSAGetLastError() == WSAEWOULDBLOCK) continue;
cout << socketVector[Index].recvBuffer << endl;
}
}
// Close WSA
}
2. Client
https://choiprogramming.tistory.com/110
[SERVER] Select
1. Select 1.1 Non-blocking Socket의 문제 - 함수의 결과를 예상X - 패킷 수신 여부와 관계없이 바로 리턴 → buf의 상태가 어떤지 알 방법X - 소켓이 Read/Write 할 수 있는 상태인지 아닌지 판별해 주는 select
choiprogramming.tistory.com
'Create Game > [Window API] Game Client & Game Server' 카테고리의 다른 글
[SERVER] IOCP (0) | 2023.01.14 |
---|---|
[SERVER] Overlapped I/O (0) | 2023.01.14 |
[SERVER] Select (1) | 2023.01.12 |
[SEVER] Non-blocking Socket (1) | 2023.01.11 |
[SEVER] UDP Server (0) | 2023.01.10 |