windows下使用IOCP实现reactor模式,使用线程池增加任务处理

#include <winsock2.h>
#include <Windows.h>
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <map>
#include <memory>

#pragma comment(lib,"ws2_32.lib")

#define BUFFER_SIZE 1024
#define PORT 8040

//客户端上下文结构,保存客户端的信息
struct Clientcontext {
    SOCKET socket;
    char buffer[BUFFER_SIZE];//缓冲区大小
    WSABUF wsabuf;           //winsock数据缓冲区的结构
    /* **重叠数据结构,异步io使用,保存IO操作的状态信息,和操作系统通信,操作系统通过
         OVERLAPPED结构的指针,跟踪管理每个异步I/O操作的进度,操作完成后,操作系统将结果
         与OVERLAPPED结构绑定,通知应用程序。
         typedef struct _OVERLAPPED {
            ULONG_PTR Internal;    // 内部使用的成员,通常用于错误或状态码
            ULONG_PTR InternalHigh; // 内部使用的成员,通常用于保存字节数
            union {
                struct {
                    DWORD Offset;  // 读写操作的文件偏移量(低32位)
                    DWORD OffsetHigh;  // 读写操作的文件偏移量(高32位)
                };
                PVOID Pointer;  // 用于 I/O 操作的指针
            };
            HANDLE hEvent;  // I/O 操作完成后触发的事件句柄
            //hEvent:这是一个事件句柄,当异步 I/O 操作完成时,系统会设置这个事件为信号状态。你可以选择使用 
            //GetOverlappedResult 或通过 WaitForSingleObject 等函数等待这个事件,来确定 I/O 操作的完成。
        } OVERLAPPED, *LPOVERLAPPED;

    */
    OVERLAPPED overlapped;   
    DWORD byteReceived;   //实际接收到的字节数
};
#include <mutex>
#include <condition_variable>
#include <queue>
#include <future>
#include <functional>
#include <type_traits>
//线程池:
class ThreadPool {
public:
    ThreadPool(int n):nthread_count(n),brun(true) {
        for (int i = 0; i < nthread_count; ++i) {
            m_vecthread.emplace_back([this]() {
                while (brun) {
                    std::function<void()>func;
                    {
                        std::unique_lock<std::mutex>lck(mtx);
                        cv.wait(lck, [this]() {
                            return !m_quetasks.empty();
                            });
                        func=std::move(m_quetasks.front());
                        m_quetasks.pop();
                    }
                    func();
                    std::this_thread::sleep_for(std::chrono::milliseconds(10));
                }
                });
        }
    }

    template<typename F,typename ...Args>
    auto Enqueue(F &&f,Args&& ...args)->std::future<std::invoke_result_t<F,Args...>> {
        using return_type = std::invoke_result_t<F, Args...>; 
        auto ptr = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f),std::forward<Args>(args)...));
        std::future<return_type> fut = ptr->get_future();

        {
            std::lock_guard<std::mutex>lck(mtx);
            //lamda表达式捕获ptr,也会让ptr的引用计数+1
            m_quetasks.emplace([ptr]() {//关于为什么要用指针,如果分布在栈上的话,当m_quetasks在别的地方调用的时候早就失效了
                (*ptr)();
                });
        }
        return fut;
    }


    ~ThreadPool() {
        while (!m_quetasks.empty()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        brun = false;
        for (auto& thread : m_vecthread)
            thread.join();
    }


private:
    int nthread_count;
    std::condition_variable cv;
    std::queue<std::function<void()>> m_quetasks;
    std::mutex mtx;
    bool brun;
    std::vector<std::thread>m_vecthread;
};

class IOCPReactor {
private:
    HANDLE iocpHandle_;
    SOCKET listenSocket_;
    bool brecAccept;
    bool isrun;
    std::thread thAccept;
public:
    IOCPReactor() :iocpHandle_(nullptr), listenSocket_(INVALID_SOCKET), brecAccept(true), isrun(true){}
    ~IOCPReactor() {
        if (iocpHandle_ != nullptr)
            CloseHandle(iocpHandle_);
        if (listenSocket_ != INVALID_SOCKET)
            closesocket(listenSocket_);
        brecAccept = false;
        thAccept.join();
        WSACleanup();//清理winsock库
    }

    bool initReactor() {
        //首先初始化winsock库
        WSADATA wsadata;
        if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) {//使用2.2版本
            std::cerr << "wassatartup failed" << std::endl;
            return false;
        }
        //创建IO完成端口
        /*
        * 参数意义:
        * 1.需要绑定的socket句柄
        * 2.完成端口句柄
        * 3.completionKey完成键,作为唯一的标识,通常是客户端上下文指针
        * 4.最大工作线程数量,如果为0,就是cpu内核数量
        */
        iocpHandle_ = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
        if (iocpHandle_ != NULL) {
            std::cerr << "iocpHandle create fail" << std::endl;
            return false;
        }
        //创建服务端监听套接字
        listenSocket_ = socket(AF_INET, SOCK_STREAM,0);//ipv4下使用tcp连接
        if (listenSocket_ == INVALID_SOCKET) {
            std::cerr << "listen socket create fail" << std::endl;
            return false;
        }
        //监听套接字绑定本机所有端口
        sockaddr_in serverAddr{};//服务器地址结构体
        serverAddr.sin_family = AF_INET;//ipv4地址族
        serverAddr.sin_port = htons(PORT); //端口号转为网络字节序
        serverAddr.sin_addr.s_addr = INADDR_ANY;//绑定到本机所有可用网络端口
        if (bind(listenSocket_, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
            std::cerr << "bind fail" << std::endl;
            return false;
        }
        if (listen(listenSocket_, SOMAXCONN) == SOCKET_ERROR)
        {
            std::cerr << "listen fail" << std::endl;
            return false;
        }
        //将服务器套接字加入到完成端口
        /*
        * 1.socket的句柄
        * 2.iocphandle句柄
        * 3.completionKey:套接字的唯一标识符
        * 4.
        */
        if (CreateIoCompletionPort((HANDLE)listenSocket_, iocpHandle_, 0, 0) == NULL) {
            std::cerr << "failed to jiarushibai" << std::endl;
            return false;
        }
        thAccept = std::move(std::thread(&IOCPReactor::acceptclients,this));
        return true;
    }

    void run() {
        while (isrun)
        {
            DWORD bytesTransferred;  // 存储传输的字节数
            ULONG_PTR completionKey; // 存储完成键(可以是客户端上下文指针)
            LPOVERLAPPED overlapped; // 存储重叠 I/O 的结构指针

            // 等待 IO 完成端口事件 (阻塞式,线程是挂起状态,也是通过overlapped的完成时间发送才响应)
            /*
            * completionkey就是当时关联IOCP时的指针,用于标记客户端的,当使用GetQueuedCompletionStatus时,他会将
            * 之前传入的指针传递给你,所以这个可以传入一个clientContext的上下文,上下文包含所有内容信息
            */
            BOOL result = GetQueuedCompletionStatus(
                iocpHandle_, &bytesTransferred, &completionKey, &overlapped, INFINITE);
            if (!result) {
                std::cerr << "getqueuedcompletion fail" << std::endl;
                continue;
            }
            if (completionKey == 0) {//新客户端连接  (因为创建listenSocket的时候给的唯一标识符是0,这个得到0说明就是listenSocket得到的消息)
                Clientcontext* clientcontext = (Clientcontext*)completionKey;
                std::cout << "new client connect" << std::endl;
                DWORD flags=0;
                WSARecv(clientcontext->socket, &clientcontext->wsabuf, 1, &clientcontext->byteReceived, &flags, &clientcontext->overlapped, NULL);
            }
            else
            {
                std::shared_ptr<Clientcontext>context_ptr(reinterpret_cast<Clientcontext*>(completionKey));                
                //Clientcontext* clientcontext = (Clientcontext*)completionKey;
                if (bytesTransferred == 0)//没有数据,客户端断开连接
                {
                    std::cerr << "client disconnected" << std::endl;
                    closesocket(context_ptr->socket);
                    
                }
                else
                {
                    context_ptr->buffer[context_ptr->byteReceived] = '\0';
                    std::cout << "receive from client" << context_ptr->buffer << std::endl;
                    /对于接收到的消息可以进行处理(建议使用线程池封装)
                    //使用lamda表达式将任务封装到线程池的队列中(也可以使用一个任务分发器,例如http请求的get/post等)
                    
                    DWORD flags = 1;
                    WSARecv(context_ptr->socket, &context_ptr->wsabuf, context_ptr->wsabuf.len, &context_ptr->byteReceived, &flags, &context_ptr->overlapped, NULL);
                }
            }
        }
    }


private:
    void acceptclients() {
        while (brecAccept){
            sockaddr_in clientAddr;
            int clientArrrlen = sizeof(clientAddr);
            //监听socket只接收连接请求,成功后返回一个用于通信的socket
            SOCKET clientSocket = accept(listenSocket_, (sockaddr*)&clientAddr, &clientArrrlen);
            if (clientSocket == INVALID_SOCKET) {
                std::cerr << "Accept failed" << std::endl;
                continue;
            }
            //创建客户端上下文与iocp关联
            Clientcontext* clientcontext = new Clientcontext();
            clientcontext->socket = clientSocket;
            clientcontext->wsabuf.buf = clientcontext->buffer;
            clientcontext->wsabuf.len = BUFFER_SIZE;
            memset(&clientcontext->overlapped, 0, sizeof(OVERLAPPED));

            //将客户端和IOCP关联上
            if (CreateIoCompletionPort((HANDLE)clientSocket, iocpHandle_, (ULONG_PTR)clientcontext, 0) == NULL)
            {
                std::cerr << "client lianjie fail" << std::endl;
                closesocket(clientSocket);
                delete clientcontext;
                continue;
            }
            //客户端关联上了IOCP,启动第一次异步接收数据
            DWORD flags = 0;
            WSARecv(clientSocket, &clientcontext->wsabuf, 1, &clientcontext->byteReceived, &flags, &clientcontext->overlapped, NULL);
        }
    }


};```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值