IOCP 는 Windows 전용 고성능 비동기 I/O 모델로, 정식 명칭은 I/O Completion Port.
IOCP 객체 (Completion Port)
HANDLE hIOCP = CreateIoCompletionPort(...);
작업 완료 알림을 넣어두는 큐
완료된 recv/send 결과가 쌓임
소켓을 IOCP에 등록
CreateIoCompletionPort(
(HANDLE)clientSocket,
hIOCP,
(ULONG_PTR)clientContext,
0
);
recv 요청
WSARecv(sock, &buf, 1, NULL, &flags, &ov, NULL);
IOCP 서버의 스레드
while (true)
{
GetQueuedCompletionStatus(
hIOCP,
&bytes,
&key,
&overlapped,
INFINITE
);
// 여기로 "작업 끝났음"이 들어옴
}
IOCP 서버 최소 동작 코드
Clientcontext - 핵심 구조체
struct ClientContext
{
SOCKET socket;
OVERLAPPED overlapped;
WSABUF wsaBuf;
char buffer[1024];
};
socket : 클라이언트 소켓
OVERLAPPED : 비동기 작업 식별자
WSABUF : recv 대상 버퍼
buffer : 실제 데이터 공간
전연 IOCP 핸들
HANDLE g_hIOCP;
Worker Thread
DWORD WINAPI WorkerThread(LPVOID)
{
while (true)
{
DWORD bytesTransferred;
ULONG_PTR completionKey;
OVERLAPPED* pOverlapped;
BOOL ret = GetQueuedCompletionStatus(
g_hIOCP,
&bytesTransferred,
&completionKey,
&pOverlapped,
INFINITE
);
ClientContext* ctx = (ClientContext*)completionKey;
if (bytesTransferred == 0)
{
closesocket(ctx->socket);
delete ctx;
continue;
}
ctx->buffer[bytesTransferred] = '\0';
printf("Recv: %s\n", ctx->buffer);
// 다시 recv 요청
ZeroMemory(&ctx->overlapped, sizeof(OVERLAPPED));
DWORD flags = 0;
WSARecv(
ctx->socket,
&ctx->wsaBuf,
1,
NULL,
&flags,
&ctx->overlapped,
NULL
);
}
}
서버 초기화 & 메인
int main()
{
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
SOCKET listenSock = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
addr.sin_addr.s_addr = INADDR_ANY;
bind(listenSock, (SOCKADDR*)&addr, sizeof(addr));
listen(listenSock, SOMAXCONN);
// IOCP 생성
g_hIOCP = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
0,
0
);
// Worker Thread 1개 (최소)
CreateThread(NULL, 0, WorkerThread, NULL, 0, NULL);
printf("IOCP Server Start\n");
while (true)
{
SOCKET clientSock = accept(listenSock, NULL, NULL);
ClientContext* ctx = new ClientContext;
ZeroMemory(ctx, sizeof(ClientContext));
ctx->socket = clientSock;
ctx->wsaBuf.buf = ctx->buffer;
ctx->wsaBuf.len = sizeof(ctx->buffer);
// 소켓을 IOCP에 연결
CreateIoCompletionPort(
(HANDLE)clientSock,
g_hIOCP,
(ULONG_PTR)ctx,
0
);
// 최초 recv 요청
DWORD flags = 0;
WSARecv(
clientSock,
&ctx->wsaBuf,
1,
NULL,
&flags,
&ctx->overlapped,
NULL
);
}
return 0;
}
IOCP에서 길이 + 데이터 프레임 처리
전송 규칙
[4바이트 길이][데이터 본문]
ClientContext 확장
struct ClientContext
{
SOCKET socket;
OVERLAPPED overlapped;
WSABUF wsaBuf;
char buffer[4096];
int recvBytes; // 지금까지 받은 바이트
int expectedBytes; // 이번에 받아야 할 총 바이트
};
IOCP에서는 “상태”를 Context에 저장한다
최초 recv 요청 (길이 4바이트)
ctx->recvBytes = 0;
ctx->expectedBytes = sizeof(int);
ctx->wsaBuf.buf = ctx->buffer;
ctx->wsaBuf.len = ctx->expectedBytes;
WSARecv(
ctx->socket,
&ctx->wsaBuf,
1,
NULL,
0,
&ctx->overlapped,
NULL
);
WorkerThread에서 프레임 처리
DWORD WINAPI WorkerThread(LPVOID)
{
while (true)
{
DWORD bytesTransferred;
ULONG_PTR key;
OVERLAPPED* ov;
GetQueuedCompletionStatus(
g_hIOCP,
&bytesTransferred,
&key,
&ov,
INFINITE
);
ClientContext* ctx = (ClientContext*)key;
if (bytesTransferred == 0)
{
closesocket(ctx->socket);
delete ctx;
continue;
}
ctx->recvBytes += bytesTransferred;
if (ctx->recvBytes < ctx->expectedBytes)
{
// 아직 부족 → 계속 recv
ctx->wsaBuf.buf = ctx->buffer + ctx->recvBytes;
ctx->wsaBuf.len = ctx->expectedBytes - ctx->recvBytes;
WSARecv(
ctx->socket,
&ctx->wsaBuf,
1,
NULL,
0,
&ctx->overlapped,
NULL
);
continue;
}
// 여기 오면 "이번 프레임 완성"
길이 프레임인지 / 데이터 프레임인지 구분
if (ctx->expectedBytes == sizeof(int))
{
int bodySize;
memcpy(&bodySize, ctx->buffer, sizeof(int));
bodySize = ntohl(bodySize);
ctx->recvBytes = 0;
ctx->expectedBytes = bodySize;
ctx->wsaBuf.buf = ctx->buffer;
ctx->wsaBuf.len = bodySize;
WSARecv(
ctx->socket,
&ctx->wsaBuf,
1,
NULL,
0,
&ctx->overlapped,
NULL
);
}
else
{
// 데이터 프레임 완성
ctx->buffer[ctx->expectedBytes] = '\0';
printf("Recv Msg: %s\n", ctx->buffer);
// 다시 길이부터 받기
ctx->recvBytes = 0;
ctx->expectedBytes = sizeof(int);
ctx->wsaBuf.buf = ctx->buffer;
ctx->wsaBuf.len = sizeof(int);
WSARecv(
ctx->socket,
&ctx->wsaBuf,
1,
NULL,
0,
&ctx->overlapped,
NULL
);
}
}
}
'C++ > Win32' 카테고리의 다른 글
| [Win32] IOCP 기반 + 길이 프레임 + Worker Thread 풀 (0) | 2025.12.27 |
|---|---|
| [Win32] IOCP Worker Thread 다중화 (0) | 2025.12.27 |
| [Win32] select 서버 (0) | 2025.12.27 |
| [Win32] 네트워크 메시지 경계 처리 - Length Prefix (0) | 2025.12.27 |
| [Win32] 서버 확장 : Thread 도입 (0) | 2025.12.27 |