비동기 I/O 시작
비동기 I/O 완료
스레드가 큐에서 작업 가져오기
GetQueuedCompletionStatus 함수는 IOCP 큐에서 완료된 작업을 가져옵니다. Device List
CreateIoCompletionPort를 통해 소켓과 Completion Port를 바인딩합니다.I/O Queue
WSAOVERLAPPED 구조체를 포함하며, 스레드가 결과를 처리할 준비가 되어 있습니다.Thread Pool
CreateIoCompletionPort: IOCP 객체를 생성하고 소켓을 등록.WSARecv 및 WSASend: 비동기 데이터 송수신.GetQueuedCompletionStatus: 큐에서 완료된 작업을 가져와 처리.Session 구조체
struct Session {
SOCKET socket = INVALID_SOCKET; // 클라이언트 소켓
char recvBuffer[BUFSIZE] = {}; // 수신 버퍼
int32 recvBytes = 0; // 수신 데이터 크기
};
OverlappedEx 구조체
WSAOVERLAPPED를 확장하여 작업 유형(Read, Write 등)을 추가로 저장합니다.struct OverlappedEx {
WSAOVERLAPPED overlapped = {}; // 기본 오버랩 구조체
int32 type = 0; // 작업 유형 (READ, WRITE 등)
};
void WorkerThreadMain(HANDLE iocpHandle)
{
while (true)
{
DWORD bytesTransferred = 0; // 전송된 바이트 수
Session* session = nullptr; // 클라이언트 세션
OverlappedEx* overlappedEx = nullptr;
BOOL ret = ::GetQueuedCompletionStatus(
iocpHandle, &bytesTransferred,
(ULONG_PTR*)&session,
(LPOVERLAPPED*)&overlappedEx, INFINITE);
iocpHandle: IOCP 핸들.bytesTransferred: 완료된 작업에서 전송된 데이터 크기.session: 작업과 연관된 클라이언트 세션.overlappedEx: 작업에 사용된 오버랩 구조체. if (ret == FALSE || bytesTransferred == 0)
{
// 연결 끊김 처리
continue;
}
assert(overlappedEx->type == IO_TYPE::READ);
cout << "Recv Data Len = " << bytesTransferred << endl;
cout << "Recv Data IOCP = " << session->recvBuffer << endl;
ret == FALSE) 또는 데이터 없음(bytesTransferred == 0) 시 연결이 끊긴 것으로 간주합니다. WSABUF wsaBuf;
wsaBuf.buf = session->recvBuffer;
wsaBuf.len = BUFSIZE;
DWORD recvLen = 0;
DWORD flags = 0;
::WSARecv(session->socket, &wsaBuf, 1, &recvLen, &flags, &overlappedEx->overlapped, NULL);
}
}
WSARecv: 비동기 데이터 수신을 요청합니다.int main()
{
SocketUtils::Init(); // Winsock 초기화
소켓 생성 및 바인딩:
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
SocketUtils::BindAnyAddress(listenSocket, 7777);
SocketUtils::Listen(listenSocket);
7777에 바인딩 및 리슨 상태로 설정.IOCP 생성:
HANDLE iocpHandle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
워커 스레드 실행:
for (int32 i = 0; i < 5; i++)
GThreadManager->Launch([=]() { WorkerThreadMain(iocpHandle); });
클라이언트 수락 루프:
while (true)
{
SOCKADDR_IN clientAddr;
int32 addrLen = sizeof(clientAddr);
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
return 0;
Session* session = new Session();
session->socket = clientSocket;
sessionManager.push_back(session);
::CreateIoCompletionPort((HANDLE)clientSocket, iocpHandle, (ULONG_PTR)session, 0);
비동기 작업 시작:
WSABUF wsaBuf;
wsaBuf.buf = session->recvBuffer;
wsaBuf.len = BUFSIZE;
OverlappedEx* overlappedEx = new OverlappedEx();
overlappedEx->type = IO_TYPE::READ;
DWORD recvLen = 0;
DWORD flags = 0;
::WSARecv(clientSocket, &wsaBuf, 1, &recvLen, &flags, &overlappedEx->overlapped, NULL);
}
- IOCP에 등록된 클라이언트 소켓에서 데이터를 비동기로 읽기 시작합니다.
---
### 클라이언트 코드 설명
#### **소켓 연결 및 데이터 전송**
1. **클라이언트 소켓 생성 및 비동기 설정**:
```cpp
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
u_long on = 1;
::ioctlsocket(clientSocket, FIONBIO, &on);
서버 연결:
while (true)
{
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
if (::WSAGetLastError() == WSAEWOULDBLOCK)
continue;
if (::WSAGetLastError() == WSAEISCONN)
break;
}
}
데이터 전송:
while (true)
{
char sendBuffer[100] = "Hello I am Client!";
::send(clientSocket, sendBuffer, sendLen, 0);
}