728x90

윤성우씨의 "열혈강의 TCP/IP 소켓 프로그래밍" 책 끝부분의 IOCP 예제를 c++형식으로 바꾸었다.


main.cpp

#include "IOCPServer.h"

int main()
{
    IOCPServer *s = new IOCPServer();

    s->Init();

    s->Run();

    delete s;
    return 0;
}


별 내용 없다.


IOCPServer.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <winsock2.h>

#define PORT        9088
#define BUFSIZE     1024
#define READ        3
#define WRITE       5

///////////////////////////////////////////////
// Structure Definition
typedef struct          // 소켓 정보를 구조체화
{
    SOCKET      hClntSock;
    SOCKADDR_IN clntAddr;
} PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

typedef struct          // 소켓의 버퍼 정보를 구조체화
{
    OVERLAPPED overlapped;
    CHAR       buffer[BUFSIZE];
    WSABUF     wsaBuf;
    int        rwMode;
} PER_IO_DATA, *LPPER_IO_DATA;


////////////////////////////////////////////////
// Class definition
class IOCPServer {
public:
    IOCPServer();
    ~IOCPServer();

    bool Run();
    bool Init();
    void Cleanup();

    bool setSocket();

    static unsigned int __stdcall _CompletionThread(void *p_this);
    UINT WINAPI CompletionThread();

private:
    HANDLE  m_hCompletionPort;

    SOCKET m_hServSock;
    SOCKADDR_IN m_servAddr;
};

//////////////////////////////////////////
// Function forward declaration
void ErrorHandling(LPCSTR pszMessage);


중간에 _CompletionThread() 멤버함수가 static인 이유는 _beginthreadex() 함수의 인자때문으로


_CompletionThread()가 호출되면 바로 CompletionThread() 멤버함수를 호출한다.


IOCPServer.cpp

#include "IOCPServer.h" IOCPServer::IOCPServer() { } IOCPServer::~IOCPServer() { closesocket(m_hServSock); WSACleanup(); } bool IOCPServer::Init() { WSAData wsaData; SYSTEM_INFO systemInfo; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { // Load Winsock 2.2 DLL ErrorHandling("WSAStartup() error!"); } // Completion Port 생성 m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); GetSystemInfo(&systemInfo); // 쓰레드를 CPU 개수*2 만큼 생성 for (int i = 0; i<systemInfo.dwNumberOfProcessors*2; i++) { _beginthreadex(NULL, 0, _CompletionThread, (LPVOID)this, 0, NULL); } setSocket(); return true; } void IOCPServer::Cleanup() { } bool IOCPServer::setSocket() { // IOCP를 사용하기 위한 소켓 생성 (Overlapped I/O 소켓, 윈도우용) m_hServSock = WSASocket(PF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); if (m_hServSock == INVALID_SOCKET) { ErrorHandling("WSASocket() error!"); } // 바인딩할 소켓 주소정보 memset(&m_servAddr, 0, sizeof(m_servAddr)); m_servAddr.sin_family = AF_INET; m_servAddr.sin_addr.s_addr = htonl(INADDR_ANY); m_servAddr.sin_port = htons(PORT); if (bind(m_hServSock, (SOCKADDR *)&m_servAddr, sizeof(m_servAddr)) == SOCKET_ERROR) { ErrorHandling("bind() error!"); } if (listen(m_hServSock, 10) == SOCKET_ERROR) { ErrorHandling("listen() error!"); } return true; } bool IOCPServer::Run() { LPPER_IO_DATA perIoData; LPPER_HANDLE_DATA perHandleData; DWORD dwRecvBytes; DWORD i, dwFlags; while (TRUE) { SOCKET hClntSock; SOCKADDR_IN clntAddr; int nAddrLen = sizeof(clntAddr); hClntSock = accept(m_hServSock, (SOCKADDR *)&clntAddr, &nAddrLen); if (hClntSock == INVALID_SOCKET) { ErrorHandling("accept() error!"); } // 연결된 클라이언트의 소켓 핸들 정보와 주소 정보를 설정 perHandleData = new PER_HANDLE_DATA; perHandleData->hClntSock = hClntSock; memcpy(&(perHandleData->clntAddr), &clntAddr, nAddrLen); // 3. Overlapped 소켓과 Completion Port 의 연결 CreateIoCompletionPort((HANDLE)hClntSock, m_hCompletionPort, (DWORD)perHandleData, 0); // 클라이언트를 위한 버퍼를 설정, OVERLAPPED 변수 초기화 perIoData = new PER_IO_DATA; memset(&(perIoData->overlapped), 0, sizeof(OVERLAPPED)); perIoData->wsaBuf.len = BUFSIZE; perIoData->wsaBuf.buf = perIoData->buffer; perIoData->rwMode = READ; dwFlags = 0; // 4. 중첩된 데이타 입력 WSARecv(perHandleData->hClntSock, // 데이타 입력 소켓 &(perIoData->wsaBuf), // 데이타 입력 버퍼 포인터 1, // 데이타 입력 버퍼의 수 &dwRecvBytes, &dwFlags, &(perIoData->overlapped), // OVERLAPPED 변수 포인터 NULL ); } return true; } unsigned int __stdcall IOCPServer::_CompletionThread(void *p_this) { IOCPServer* p_IOCPServer = static_cast<IOCPServer*>(p_this); p_IOCPServer->CompletionThread(); // Non-static member function! return 0; } UINT WINAPI IOCPServer::CompletionThread() { HANDLE hCompletionPort = (HANDLE)m_hCompletionPort; SOCKET hClntSock; DWORD dwBytesTransferred; LPPER_HANDLE_DATA perHandleData; LPPER_IO_DATA perIoData; DWORD dwFlags; while (TRUE) { // 5. 입.출력이 완료된 소켓의 정보 얻음 GetQueuedCompletionStatus(hCompletionPort, // Completion Port &dwBytesTransferred, // 전송된 바이트 수 (LPDWORD)&perHandleData, (LPOVERLAPPED *)&perIoData, INFINITE); //printf("GQCS error code : %d (TID : %d)\n", GetLastError(), GetCurrentThreadId()); if (perIoData->rwMode == READ) { puts("message received!"); if (dwBytesTransferred == 0) // 전송된 바이트가 0일때 종료 (EOF 전송 시에도) { puts("socket will be closed!"); closesocket(perHandleData->hClntSock); delete perHandleData; delete perIoData; continue; } // 6. 메세지. 클라이언트로 에코 memset(&(perIoData->overlapped), 0, sizeof(OVERLAPPED)); perIoData->wsaBuf.len = dwBytesTransferred; perIoData->rwMode = WRITE; WSASend(perHandleData->hClntSock, &(perIoData->wsaBuf), 1, NULL, 0, &(perIoData->overlapped), NULL); // RECEIVE AGAIN perIoData = new PER_IO_DATA(); memset(&(perIoData->overlapped), 0, sizeof(OVERLAPPED)); perIoData->wsaBuf.len = BUFSIZE; perIoData->wsaBuf.buf = perIoData->buffer; perIoData->rwMode = READ; dwFlags = 0; WSARecv(perHandleData->hClntSock, &(perIoData->wsaBuf), 1, NULL, &dwFlags, &(perIoData->overlapped), NULL); } else // WRITE Mode { puts("message sent!"); delete perIoData; } } return 0; } void ErrorHandling(LPCSTR pszMessage) { fputs(pszMessage, stderr); fputc('\n', stderr); exit(1); }


CreateIoCompletionPort() 함수는 특이하게도 이름처럼 completion port(이하 cp)를 생성하는데도 쓰지만


cp에 소켓을 등록하는데에도 쓰인다.


Run() 멤버 함수는 호출되면 클라이언트의 연결을 받아들이고 cp에 등록 후 WSARecv()를 호출하는 과정을 반복한다.


그리고 CompletionThread() 에서 각 스레드는 GetQueuedCompletionStatus() 를 호출하고 입출력이 완료된 소켓이


큐에 올라올때까지 대기한다. 입출력이 완료된(입력이 완료된) 소켓이 올라오면 받은 데이터를 그대로 WSASend()를 이용하여 보내주고


다시 WSARecv()호출한다.


실행한 모습

728x90

'프로그래밍 > C++' 카테고리의 다른 글

[C++] Reference Counting  (2) 2020.11.04
힙(Heap)과 완전 이진 트리(Complete binary tree)  (1) 2019.10.31
[c] SHA-1 구조 및 코드  (2) 2017.12.09
[c] AES 구조 및 코드  (2) 2017.11.15
[c] DES 구조 및 코드  (21) 2017.10.26

+ Recent posts