IOCP完成端口

#include <ws2tcpip.h>     
#include <mswsock.h>      
#include <windows.h>      
#include <iostream>        
#include <vector>         
#include <thread>          
#include <mutex>           
#include <condition_variable> 
#include <queue>          
#include <functional>    

#pragma comment(lib, "ws2_32.lib") // 链接Winsock库
#pragma comment(lib, "mswsock.lib") // 链接MSWSock库

#define PORT "8888"       
#define MAX_CLIENTS 100    // 定义最大客户端数量
#define BUFFER_SIZE 4096   // 定义缓冲区大小

// 操作类型枚举,用于区分不同的I/O操作
enum OperationType {
    OP_ACCEPT,             // 接受连接操作
    OP_READ,               // 读取数据操作
    OP_WRITE               // 写入数据操作
};

// 重叠结构,扩展了WSAOVERLAPPED以包含更多自定义信息
struct OverlappedEx {
    WSAOVERLAPPED overlapped;  // 标准重叠结构
    OperationType opType;      // 操作类型
    char buffer[BUFFER_SIZE];  // 数据缓冲区
    WSABUF wsabuf;            // Windows Sockets缓冲区结构
    SOCKET clientSocket;      // 客户端套接字
};

class IOCPChatServer {      // IOCP服务器类
private:
    SOCKET listenSocket;     // 监听套接字
    HANDLE completionPort;   // 完成端口句柄
    std::vector<std::thread> workerThreads; // 工作线程向量
    bool running;            // 服务器运行状态标志
    int threadCount;         // 工作线程数量

public:
    // 构造函数,初始化服务器对象
    IOCPChatServer(int numThreads = 4) : listenSocket(INVALID_SOCKET), completionPort(NULL), running(false), threadCount(numThreads) {
        // 初始化Winsock
        WSADATA wsaData;    
        int result = WSAStartup(MAKEWORD(2, 2), &wsaData); 
        if (result != 0) {   // 检查启动是否成功
            std::cerr << "WSAStartup failed: " << result << std::endl; 
            return;       
        }
    }

    ~IOCPChatServer() {
        Stop();            
        WSACleanup();     
    }

    // 初始化
    bool Initialize() {
        // 创建监听套接字
        ADDRINFO hints, * addrInfo = NULL; // 地址信息结构
        ZeroMemory(&hints, sizeof(hints)); // 清零hints结构
        hints.ai_family = AF_INET; // 设置地址族为IPv4
        hints.ai_socktype = SOCK_STREAM; // 设置套接字类型为流式
        hints.ai_protocol = IPPROTO_TCP; // 设置协议为TCP
        hints.ai_flags = AI_PASSIVE; // 设置为被动模式(用于监听)

        int result = getaddrinfo(NULL, PORT, &hints, &addrInfo); 
        if (result != 0) {  
            std::cerr << "getaddrinfo failed: " << result << std::endl; 
            return false;   
        }

        // 创建套接字
        listenSocket = socket(addrInfo->ai_family, addrInfo->ai_socktype, addrInfo->ai_protocol); 
        if (listenSocket == INVALID_SOCKET) { 
            std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl; 
            freeaddrinfo(addrInfo); 
            return false;    
        }

        // 绑定套接字到地址
        result = bind(listenSocket, addrInfo->ai_addr, (int)addrInfo->ai_addrlen);
        if (result == SOCKET_ERROR) {
            std::cerr << "bind failed with error: " << WSAGetLastError() << std::endl;
            freeaddrinfo(addrInfo);
            closesocket(listenSocket); 
            return false;   
        }
        freeaddrinfo(addrInfo); // 释放地址信息

        // 创建完成端口
        completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        if (completionPort == NULL) { 
            std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl;
            closesocket(listenSocket);
            return false;    
        }

        // 将监听套接字关联到完成端口
        HANDLE ret = CreateIoCompletionPort((HANDLE)listenSocket, completionPort, (ULONG_PTR)listenSocket, 0); 
        if (ret == NULL) {  
            std::cerr << "CreateIoCompletionPort associate failed: " << GetLastError() << std::endl; 
            CloseHandle(completionPort);
            closesocket(listenSocket); 
            return false;    // 返回失败
        }

        // 开始监听连接
        if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
            std::cerr << "listen failed with error: " << WSAGetLastError() << std::endl;
            closesocket(listenSocket); 
            return false;  
        }

        std::cout << "Server listening on port " << PORT << std::endl;
        return true;        
    }

    // 启动服务器
    void Start() {
        // 检查初始化是否成功
        if (!Initialize()) { 
            return;         
        }

        running = true;  

        // 启动工作线程
        for (int i = 0; i < threadCount; ++i) { // 循环创建工作线程
            workerThreads.emplace_back(&IOCPChatServer::WorkerThread, this); // 创建并启动工作线程
        }

        // 启动接受连接的循环
        AcceptLoop();      
    }

    void Stop() {
        if (!running) return;
        running = false;   

        // 关闭监听套接字以中断accept
        if (listenSocket != INVALID_SOCKET) { // 检查监听套接字是否有效
            closesocket(listenSocket); 
            listenSocket = INVALID_SOCKET; // 设置为无效值
        }

        // 投递NULL事件唤醒所有工作线程
        // 循环向每个工作线程投递停止信号
        for (int i = 0; i < threadCount; ++i) { 
            PostQueuedCompletionStatus(completionPort, 0, 0, NULL);
        }

        // 等待工作线程结束
        for (auto& t : workerThreads) {
            if (t.joinable()) { 
                t.join();    
            }
        }

        if (completionPort) { 
            CloseHandle(completionPort); 
            completionPort = NULL; 
        }
    }

private:

    void AcceptLoop() {
        while (running) {   
            SOCKET clientSocket = accept(listenSocket, NULL, NULL); 
            if (clientSocket == INVALID_SOCKET) { 
                if (running) {
                    std::cerr << "accept failed: " << WSAGetLastError() << std::endl; // 输出错误信息
                }
                break;       // 退出循环
            }

            // 关联新客户端套接字到完成端口
            HANDLE ret = CreateIoCompletionPort((HANDLE)clientSocket, completionPort, (ULONG_PTR)clientSocket, 0);
            if (ret == NULL) { 
                std::cerr << "Associate client socket with IOCP failed: " << GetLastError() << std::endl; 
                closesocket(clientSocket); 
                continue;  
            }

            // 设置套接字为非阻塞模式
            u_long nonBlocking = 1; // 非阻塞标志
            ioctlsocket(clientSocket, FIONBIO, &nonBlocking); 

            // 投递接收操作
            PostReceive(clientSocket); // 为客户端投递接收操作
        }
    }

    // 工作线程函数
    void WorkerThread() {
        DWORD bytesTransferred; // 传输的字节数
        ULONG_PTR completionKey; // 完成键
        OVERLAPPED* pOverlapped; // 重叠结构指针

        while (running) {  
            BOOL result = GetQueuedCompletionStatus( // 从完成端口获取完成事件
                completionPort,  // 完成端口句柄
                &bytesTransferred, // 传输字节数的输出参数
                &completionKey, // 完成键的输出参数
                &pOverlapped,   // 重叠结构的输出参数
                INFINITE        // 等待超时时间(无限等待)
            );

            if (!running) break;

            // 检查是否是停止信号
            if (pOverlapped == NULL) { 
               // 当我们投递NULL事件唤醒线程时会发生这种情况
                continue;      // 继续下一次循环
            }

            // 将重叠结构转换为扩展重叠结构
            OverlappedEx* pOverEx = (OverlappedEx*)pOverlapped; 

            if (!result) {   
                DWORD error = GetLastError(); 
                std::cout << "GetQueuedCompletionStatus failed, error: " << error << std::endl;

                closesocket(pOverEx->clientSocket); // 关闭客户端套接字
                delete pOverEx;  // 释放扩展重叠结构内存
                continue;      // 继续处理下一个事件
            }

            // 检查是否是连接断开
            if (bytesTransferred == 0 && (pOverEx->opType == OP_READ || pOverEx->opType == OP_WRITE)) { 
                std::cout << "Client disconnected." << std::endl; // 输出断开连接信息
                closesocket(pOverEx->clientSocket); // 关闭客户端套接字
                delete pOverEx;  // 释放扩展重叠结构内存
                continue;      // 继续处理下一个事件
            }

            switch (pOverEx->opType) { // 根据操作类型处理完成事件
            case OP_READ:  // 读取操作完成
                HandleRead(pOverEx, bytesTransferred);
                break;     
            case OP_WRITE: // 写入操作完成
                HandleWrite(pOverEx, bytesTransferred); 
                break;   
            default:       
                std::cerr << "Unknown operation type!" << std::endl; // 输出错误信息
                break;    
            }
        }
    }

    // 处理读取操作完成
    void HandleRead(OverlappedEx* pOverEx, DWORD bytesTransferred) {
        pOverEx->buffer[bytesTransferred] = '\0'; // 在接收数据末尾添加字符串结束符
        std::string message = "Echo: " + std::string(pOverEx->buffer, bytesTransferred); // 构建回显消息

        std::cout << "Received: " << pOverEx->buffer << " from client " << pOverEx->clientSocket << std::endl; // 输出接收信息

        ZeroMemory(&(pOverEx->overlapped), sizeof(pOverEx->overlapped)); // 重置重叠结构
        pOverEx->opType = OP_WRITE; // 设置操作类型为写入
        strcpy_s(pOverEx->buffer, message.c_str()); // 复制回显消息到缓冲区
        pOverEx->wsabuf.buf = pOverEx->buffer; // 设置WSABUF缓冲区指针
        pOverEx->wsabuf.len = static_cast<ULONG>(message.length()); // 设置WSABUF缓冲区长度

        DWORD flags = 0;     // 传输标志
        // 发送数据
        int sendResult = WSASend(pOverEx->clientSocket, &(pOverEx->wsabuf), 1, NULL, flags, &(pOverEx->overlapped), NULL);
        if (sendResult == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { // 检查发送是否失败(排除异步操作挂起的情况)
            std::cerr << "WSASend failed: " << WSAGetLastError() << std::endl; // 输出错误信息
            closesocket(pOverEx->clientSocket); // 关闭客户端套接字
            delete pOverEx;  // 释放扩展重叠结构内存
        }
        // 如果是WSA_IO_PENDING,操作挂起稍后完成
    }

    void HandleWrite(OverlappedEx* pOverEx, DWORD bytesTransferred) {
        PostReceive(pOverEx->clientSocket); // 为客户端投递下一个接收操作
    }

    // 投递接收操作
    void PostReceive(SOCKET clientSocket) {
        OverlappedEx* pOverEx = new OverlappedEx(); // 创建新的扩展重叠结构
        ZeroMemory(pOverEx, sizeof(OverlappedEx)); // 清零扩展重叠结构
        pOverEx->opType = OP_READ; // 设置操作类型为读取
        pOverEx->clientSocket = clientSocket; // 设置客户端套接字
        pOverEx->wsabuf.buf = pOverEx->buffer; // 设置WSABUF缓冲区指针
        pOverEx->wsabuf.len = BUFFER_SIZE; // 设置WSABUF缓冲区长度

        DWORD flags = 0;     // 接收标志
        // 投递接收操作
        int recvResult = WSARecv(clientSocket, &(pOverEx->wsabuf), 1, NULL, &flags, &(pOverEx->overlapped), NULL);
        if (recvResult == SOCKET_ERROR && WSAGetLastError() != WSA_IO_PENDING) { // 检查接收操作是否失败(排除异步操作挂起的情况)
            std::cerr << "WSARecv failed: " << WSAGetLastError() << std::endl; // 输出错误信息
            closesocket(clientSocket); // 关闭客户端套接字
            delete pOverEx;  // 释放扩展重叠结构内存
        }
        // 如果是WSA_IO_PENDING,操作挂起稍后完成
    }
};

int main() {
    IOCPChatServer server(4); // 创建服务器对象,使用4个工作线程
    server.Start();
    server.Stop();
    return 0;
}
posted @ 2019-04-09 22:47  osbreak  阅读(214)  评论(0)    收藏  举报