나만의 작은 도서관
[TIL][C++] 250428 MMO 서버 개발 6일차: 다시 정리하는 IOCP 함수, WSA___함수, ___Ex 함수 본문
Today I Learn
[TIL][C++] 250428 MMO 서버 개발 6일차: 다시 정리하는 IOCP 함수, WSA___함수, ___Ex 함수
pledge24 2025. 4. 29. 16:40주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다.
다시 정리하는 IOCP 함수
- IOCP 함수는 크게 3가지로, CICP, GQCS, PQCS가 있다. 이 3가지 함수를 통해 IOCP의 대부분의 기능을 사용할 수 있다. (여기서 PQCS는 따로 다루지 않는다.)
CICP 함수
CreateIoCompletionPort(1, 2, 3, 4)
- 비동기 I/O 소켓 핸들: 클라이언트 소켓을 HANDLE로 캐스팅해서 넣는다.
- IOCP핸들: 만들어진 IOCP의 핸들을 넣는다.
- 키: 연결한 장치에 대한 고유 키를 넣는다. 아무거나 넣어도 되지만 보통 해당 클라이언트 소켓의 세션 주소를 넣는다.
- CP 처리 쓰레드 개수: CP에 접근하는 최대 쓰레드 수를 지정한다. 0으로 설정하면, 기본값으로 프로세서 수만큼 설정한다.
// IocpHandle 생성
HANDLE IocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
// 존재하는 IocpHandle에 clientSocket 등록.
HANDLE IocpHandle2 = CreateIoCompletionPort(clientSock, IocpHandle, 0, 0);
- CICP의 용도는 2가지로, 첫 번째는 IocpHandle 생성, 두 번째는 존재하는 IocpHandle에 clientSocket을 등록하는 것이다.
- 반환 값은 IocpHandle로, 위 코드에서 IocpHandle과 IocpHandle2은 같다.
GQCS 함수
GetQueuedCompletionStatus(1, 2, 3, 4, 5)
- IOCP 핸들: 이미 존재하는 IOCP 핸들을 넣는다.
- 처리 완료한 바이트 수: 처리 완료한 네트워크 이벤트의 바이트 수
- 키: CICP에서 넣어준 키값
- Overlapped 구조체: 내부에서 사용
- timeout: CP에 완료 패킷을 받는 최대 대기 시간.
클라이언트 소켓의 세션을 넘겨주고 받는 2가지 방법
- key인자로 session 넘겨준 다음, CICP에서 넘겨준 session을 GQCS에서 받는다.
CreateIoCompletionPort(1, 2, &session, 4)
ULONG_PTR session = 0;
GetQueuedCompletionStatus(1, 2, &session, 4, 5)
- 정석적인 방법이다.
- 키를 사용하지 않는 대신, 확장된 Overlapped 구조체에 session 주소를 넣어 GQCS에서 Overlapped 구조체를 통해 session을 얻는다.
CreateIoCompletionPort(1, 2, 0, 4)
WSARecv(1, 2, 3, 4, 5, OverlappedEx, 7)
OverlappedEx* overlappedEx;
GetQueuedCompletionStatus(1, 2, 3, reinterpret_cast<LPOVERLAPPED*>(overlappedEx), INFINITE);
SessionRef = overlappedEx->session;
- 키값 없이 Overlapped 구조체 하나로 세션 주소를 얻을 수 있기 때문에 구조가 단순해진다는 장점이 있다.
다시 정리하는 비동기 네트워크 이벤트 함수(WSA___)
WSARecv()
WSARecv(1, 2, 3, 4, 5, 6, 7)
- 비동기 I/O 소켓: 클라이언트 소켓을 넣는다.
- 수신 버퍼 주소: 해당 소켓으로부터 수신된 데이터를 복사할 버퍼 주소를 넣는다.
- 수신 버퍼 개수: 수신 버퍼의 개수를 넣는다. (기본값 1)
- 수신된 데이터 길이: 수신된 데이터의 바이트 수
- 플래그: 상세 옵션 설정용
- Overlapped 구조체: 내부에서 사용하는 구조체
- 완료 루틴: 수신 완료 시 이어서 OS가 호출할 콜백함수
WSASend()
WSASend(1, 2, 3, 4, 5, 6, 7)
- 비동기 I/O 소켓: 클라이언트 소켓을 넣는다.
- 송신 버퍼 주소: 해당 소켓의 원격 주소에게 송신할 데이터를 복사할 버퍼 주소를 넣는다.
- 송신 버퍼 개수: 수신 버퍼의 개수를 넣는다. (기본값 1, Scatter-Gather과 관련 있음)
- 송신한 데이터 길이: 송신한 데이터 바이트 수
- 플래그: 상세 옵션 설정용
- Overlapped 구조체: 내부에서 사용하는 구조체
- 완료 루틴: 송신 완료 시 이어서 OS가 호출할 콜백함수
런타임에 API에서 가져온 확장 함수(___EX)
- 해당 함수들은 런타임에 가져온 확장 함수들로, 비동기로 실행할 수 있는 함수들이며 작업 완료 시 CP의 완료 패킷이 들어간다.
- 모든 네트워크 이벤트들을 GQCS를 통해 관리하기 위해 사용한다.
ConnectEx(클라이언트 전용)
ConnectEx(1, 2, 3, 4, 5, 6, 7)
- 비동기 I/O 소켓: 클라이언트 소켓을 넣는다.
- 타겟 서버 주소: 연결할 서버 주소를 넣는다.
- 주소 크기: 2번에 해당하는 변수 크기를 넣는다. ex. sizeof(sockAddr)
- connect 성공 시, 전송할 데이터가 담긴 버퍼 주소
- 연결이 되자마자 보내는 데이터로, 옵션이다.
- 4번의 버퍼 데이터 길이(바이트 수)
- 4번의 버퍼에 들어있는 데이터의 전송된 데이터 길이
- Overlapped 구조체
DisconnectEx
DisconnectEx(1, 2, 3, 4)
- 비동기 I/O 소켓: 연결된 소켓 핸들
- Overlapped 구조체
- 플래그: 상세 옵션 설정용
- 예약된? 변수: 0으로 설정해야 함
AcceptEx
AcceptEx(1, 2, 3, 4, 5, 6, 7, 8)
- listen 소켓: 수신 대기 소켓
- 연결을 수락할 소켓(accept socket): 미리 만든 소켓. accept 성공 시 해당 소켓에 바인딩된다.
- 서버의 로컬 주소 및 클라이언트 원격 주소를 저장할 버퍼 주소
// 3번 버퍼 생김새
// recvData: 연결 성공 시, 같이 날라온 데이터
// addr: 서버의 로컬 주소 및 클라이언트 원격 주소
// [[-----recvData-----][----addr-----]]
- 3번 버퍼의 수신 데이터(recvData) 바이트 수
- 0으로 설정 시, accept 성공 시 추가 수신 작업 발생 X
- 로컬 주소 정보를 저장하기 위한 예약된 바이트 크기
- 사용 중인 전송 프로토콜 + 16 바이트 크기를 가짐
- sizeof(ex. SOCKADDR_IN) + 16
- 원격 주소 정보를 저장하기 위한 예약된 바이트 크기. 0일 수 없음
- 사용 중인 전송 프로토콜 + 16 바이트 크기를 가짐
- sizeof(ex. SOCKADDR_IN) + 16
- 동기적 함수일 경우 변경되는 값. recvData가 수신될 때까지 PENDING 반환. 수신 완료 시, 수신된 바이트 수
- Overlapped 구조체