IOCP는 입력과 출력을 구분시켜주지 않는다. 단지 완료되었다는 사실만 알려준다. 따라서 별도로 입력과 출력을 진행한 정보를 담아야한다.
// 생성
HANDLE CreateIoCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads);
// 성공 시 CP 오브젝트의 핸들이 반환, 실패시 NULL
// 순서대로 INVALID_HANDLE_VALUE, NULL, 0, IO를 담당할 쓰레드 개수를 전달한다.
// 소켓 연결도 위의 함수를 사용한다, 소켓이 연결되면 연결되 소켓을 기반으로 하는 Overlapped IO가 완료되면 아래 함수가 반환된다.
// 순서대로 소켓 핸들, CP 오브젝트 핸들, 완료된 IO 정보를 담을 공간, 마지막은 무시된다.
// 완료된 IO 확인
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytes, PULONG_PTR lpCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);
// lpCompletionKey가 Create-의 3번째에 들어간다.
// lpOverlapped는 WSASend, WSARecv에 전달되는 주소 값이다.
// 이 함수를 통해서 IO 완료를 확인하는데, 어떠한 쓰레드라도 호출할 수 있다. 다만 실제로 응답을 받는 쓰레드 수는 지정한 최대 쓰레드 수를 넘지 않는다.