1. Overlapped I/O
1.1 Overlapped I/O 의미
- 하나의 스레드에서 둘 이상의 패킷 데이터를 통신용 소켓에 송수신
- 여러 개의 소켓에 입출력이 중첩된 상황
1.2 Non-blocking Code 수정
https://choiprogramming.tistory.com/106
[SEVER] Non-blocking Socket
1. Blocking Socket 1.1 Blocking - accept, connect, recv, send - 대부분 소켓 관련 함수들은 호출 스레드를 블로킹 상태로 만듦 - 블로킹 상태: 현재 스레드가 더 이상 코드를 진행하지 않고 block 상태로 멈춰 있
choiprogramming.tistory.com
1.3 Overlapped I/O
- overlapped 구조체 생성
struct Session
{
SOCKET socket = INVALID_SOCKET;
char recvBuffer[MAX_BUFFER] = {};
WSAOVERLAPPED overlapped;
};
- Base Code
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
while (true)
{
// accept
}
// Close WSA
}
- 이벤트 핸들 오브젝트의 시그널 상태를 Overlapped 구조체에 알릴 이벤트 생성
- 소켓의 입출력 작업이 완료되면 OS는 애플리케이션이 등록한 이벤트 객체의 신호 상태를 바꿈
- 애플리케이션은 이를 바탕으로 이벤트 객체를 관찰함으로써 작업 완료를 감지
- OVERLAPPED 구조체에 Event 객체를 등록
- 비동기 입출력 함수에서 이 구조체 등록된 이벤트를 통해 현재 완료된 입출력의 상태 정보를 감지
- 소켓의 버퍼 정보를 담고있는 구조체 (wsabuf)
- 비동기 입출력 함수인 WSASend, WSARecv는 이 구조체에 들어있는 버퍼의 정보를 사용한다.
- WSABUF구조체에 전송할 버퍼의 종류와 사이즈를 넣어준다.
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
while (true)
{
// accept
{
// 클라이언트 데이터 생성
Session session = Session{ clientSocket };
// 이벤트 핸들 오브젝트의 시그널 상태를 Overlapped 구조체에 알리는 이벤트 생성
WSAEVENT hEvent = WSACreateEvent();
// 현재 완료된 입출력의 상태 정보를 감지
session.overlapped.hEvent = hEvent;
while (true)
{
// WSABUF구조체에 전송할 버퍼의 종류와 사이즈를 넣음
WSABUF wsaBuf;
wsaBuf.len = MAX_BUFFER; // 패킷 사이즈 설정
wsaBuf.buf = session.recvBuffer; // 패킷 종류 설정
// 데이터 저장할 변수 추가
DWORD dwRecvBytes = 0;
DWORD dwFlag = 0;
}
}
// Close WSA
}
- 접속된 상대방의 소켓으로부터 전송된 패킷 데이터를 받음
- socket: 접속된 소켓 지정
- lpBuffers: WSABUF 구조체 버퍼 포인터
- dwBufferCount: WSABUF 버퍼 카운터
- lpNumberOfBytesRecvd: Recv 완료 후 함수의 호출로 읽어낸 데이터의 바이트 수
- lpFlags: 입출력 Flag 설정, 이 플래그를 통해 WSARecv가 구체적으로 동작하게 될 작업 명시
- LPWSAOVERLAPPED: OVERLAPPED 구조체 포인터
- LPWSAOVERLAPPED_COMPLETION_ROUTINE: 컴플리트 루틴 (TODO)
int WSARecv (SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRecvd,
LPDWORD lpFlags, LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE);
- 에러 코드가 WSA_IO_PENDING이면 중첩 연산이 성공적으로 진행된 것
- 모든 값 통과시 출력
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
while (true)
{
// accept
// 클라이언트 데이터 생성, 이벤트 생성
while (true)
{
// 현재 완료된 입출력의 상태 정보를 감지 및 전송할 버퍼의 종류와 사이즈 입력
// 접속된 상대방의 소켓으로부터 전송된 패킷 데이터를 받음
if (WSARecv(session.socket, &wsaBuf, 1, &dwRecvBytes, &dwFlag, &session.overlapped, nullptr) == SOCKET_ERROR)
{
// 에러 코드가 WSA_IO_PENDING이면 중첩 연산이 성공적으로 진행된 것
if (WSAGetLastError() == WSA_IO_PENDING)
{
}
else continue;
}
cout << session.recvBuffer << endl;
}
}
// Close WSA
}
- 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지
- 오버랩 연산의 결과를 반환
- lpcbTransfer: 실제로 송수신된 바이트의 개수를 받는 포인터
- fWait
- FALSE: WSA_IO_INCOMPLETE 에러 코드를 반환
- TRUE: Overlapped I/O가 완료될 때까지 대기
- lpdwFlags: 작업 완료 상태를 가진 플래그를 수신할 포인터
BOOL WSAGetOverlappedResult (
SOCKET s, LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer, BOOL fWait,LPDWORD lpdwFlags
);
int main()
{
// Init WSA, Socket Create, Server IP/HOST
// Non-Blocking Socket, BIND, LISTEN
while (true)
{
// accept
// 클라이언트 데이터 생성, 이벤트 생성
while (true)
{
// 현재 완료된 입출력의 상태 정보를 감지 및 전송할 버퍼의 종류와 사이즈 입력
// 접속된 상대방의 소켓으로부터 전송된 패킷 데이터를 받음
if (WSARecv(session.socket, &wsaBuf, 1, &dwRecvBytes, &dwFlag, &session.overlapped, nullptr) == SOCKET_ERROR)
{
// 에러 코드가 WSA_IO_PENDING이면 중첩 연산이 성공적으로 진행된 것
if (WSAGetLastError() == WSA_IO_PENDING)
{
// 이벤트 상태가 시그널 상태가 될때 까지 대기 및 에빈트 객체의 신호 상태를 감지
::WSAWaitForMultipleEvents(1, &hEvent, TRUE, WSA_INFINITE, FALSE);
// 감지가 된다면 오버랩 연산의 결과를 반환
::WSAGetOverlappedResult(session.socket, &session.overlapped, &dwRecvBytes, FALSE, &dwFlag);
}
else continue;
}
cout << session.recvBuffer << endl;
}
}
// Close WSA
}
1.4 Overlapped I/O Callback
struct Session
{
WSAOVERLAPPED overlapped;
SOCKET socket = INVALID_SOCKET;
char recvBuffer[MAX_BUFFER] = {};
};
- WSARecv을 실행하고 완료가 된다면 RecvCompRoutine 함수 실행
- 이때 PENDING상태이면 SleepEx를 사용하여 Alertable 상태 변경
- I/O 연산이 완료되면 완료 루틴을 실행해야 하는데 이 완료 루틴이 실행되는 타이밍을 결정지을 수 있음
- 완료 루틴을 실행하라고 알려주게 됨
if (WSARecv(session.socket, &wsaBuf, 1, &dwRecvBytes, &dwFlag, &session.overlapped, RecvCompRoutine) == SOCKET_ERROR)
{
// 에러 코드가 WSA_IO_PENDING이면 중첩 연산이 성공적으로 진행된 것
if (WSAGetLastError() == WSA_IO_PENDING)
{
SleepEx(INFINITE, true);
}
else continue;
}
- RecvCompRoutine
- WSARecv가 완료가 된다면 SleepEx로 부터 완료 루틴을 실행
- Overlapped의 주소를 받아 세션을 생성한 후 출력
void CALLBACK RecvCompRoutine(DWORD dwError, DWORD szRecvBytes, LPWSAOVERLAPPED lpOverlapped, DWORD flags)
{
Session* session = reinterpret_cast<Session*>(lpOverlapped);
cout << session->recvBuffer << endl;
}
2. Client
https://choiprogramming.tistory.com/112
[SERVER] WSAEvent Select
1. WSAEventSelect (Windows Socket Async) 1.1 WSAEventSelect 의미 - 입출력 함수를 안전하게 호출할 수 있는 시점을 운영체제가 알려줌 - 단순한 입출력 방식보다 편리하게 여러개의 소켓을 처리 - 운영체제에
choiprogramming.tistory.com
'Create Game > [Window API] Game Client & Game Server' 카테고리의 다른 글
[GameServer] 1. IOCP Server Core & Accept (0) | 2023.01.17 |
---|---|
[SERVER] IOCP (0) | 2023.01.14 |
[SERVER] WSAEvent Select (0) | 2023.01.13 |
[SERVER] Select (1) | 2023.01.12 |
[SEVER] Non-blocking Socket (1) | 2023.01.11 |