IOCP完成端口管理

WINDOWS下的异步非阻塞的模型:
线程池完成高并发,共享内存完成非阻塞
在这里插入图片描述

ctcpnet.h

#ifndef CTCPNET_H
#define CTCPNET_H
#include <winsock2.h>
#include <windows.h>
#include <process.h>
#include <list>
#include <map>
#include <iostream>
#include <QDebug>
#include <mswsock.h>
using namespace  std;
#define MAXNUM 64
#define MAXCONTENT 4096
enum NetType{NT_ACCEPT,NT_READ};
//LPOVERLAPPED返回结构体首地址,所以定义结构体时一定要先声明OVERLAPPED m_olp
struct Mysocket
{
    OVERLAPPED m_olp;//事件 必须要这么写 定义结构体成员顺序要注意
    SOCKET m_sock;   //waiter
    char m_szbuf[MAXCONTENT]; //缓冲区
    NetType m_nType;//网络事件
};

class CTCPNet
{
public:
    CTCPNet();
public:
    //初始化网络
    bool InitNetWork();
    void UnInitNetWork();
    //收发数据
    bool SendData(SOCKET sock,char *szbuf,int nlen);
    void RecvData();
    bool PostAccept();
public:
    static unsigned _stdcall ThreadProc(void*);//全局变成局部
    static unsigned _stdcall ThreadRecv(void*);
private:
    SOCKET m_sock;
    HANDLE m_thread;
    bool m_flagquit;
    list<HANDLE> m_lstThread;//用来存放创建线程返回的句柄
    map<unsigned int,SOCKET> m_mapIdToSock;
    HANDLE m_hIOCP;
    list<Mysocket*> m_lstMysocket;
public:
    HANDLE m_aryhEvent[MAXNUM];
    SOCKET m_arySocket[MAXNUM];
    int m_nEventNum;

};

#endif // CTCPNET_H

ctcpnet.cpp

#include "ctcpnet.h"
#include <winsock2.h>
#include <windows.h> //winsock.h
#include <iostream>
#include <QDebug>
using namespace  std;

CTCPNet::CTCPNet()
{
    m_sock = NULL;
    m_flagquit = true;
    m_nEventNum = 0;
    ZeroMemory(m_aryhEvent,sizeof(m_aryhEvent));
    ZeroMemory(m_arySocket,sizeof(m_arySocket));
}

bool CTCPNet::InitNetWork()
{
    //1.选择种类 火锅 韩餐  烤肉--
      WORD wVersionRequested;
      WSADATA wsaData;
      int err;

      wVersionRequested = MAKEWORD(2, 2);

      err = WSAStartup(wVersionRequested, &wsaData);
      if (err != 0) {

          printf("WSAStartup failed with error: %d\n", err);
          return false;
      }

      if (LOBYTE(wsaData.wVersion) != 2 ||
          HIBYTE(wsaData.wVersion) != 2)
      {
          UnInitNetWork();
          return false;
      }
      else
          printf("The Winsock 2.2 dll was found okay\n");

      //2.雇个店长--
      m_sock =  socket(AF_INET,SOCK_STREAM,0);
      if(m_sock == INVALID_SOCKET)
      {
          UnInitNetWork();
          return false;
      }

      //3.找个地方
      sockaddr_in addrserver;
      addrserver.sin_family = AF_INET;
      addrserver.sin_port = htons(8899);
      addrserver.sin_addr.S_un.S_addr = 0;
     if(SOCKET_ERROR == bind(m_sock,( const sockaddr *)&addrserver,sizeof(addrserver)))
     {
         UnInitNetWork();
         return false;
     }
      //4.店长宣传
     if(SOCKET_ERROR == listen(m_sock,10))
     {
         UnInitNetWork();
         return false;
     }

     //1.创建IOCP
     m_hIOCP=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,NULL);
     if (m_hIOCP == NULL){
         UnInitNetWork();
         return false;
     }
     //2.将socketlisten交给完成端口管理
     CreateIoCompletionPort((HANDLE)m_sock,//交给完成端口管理的套接字
                            m_hIOCP,//完成端口句柄
                            (ULONG_PTR)m_sock,//完成键
                            0);//线程的个数 默认和系统处理器一样的个数
     //3.创建一堆waiter,交给socklisten 准备好了
     //获取cpu*2
     SYSTEM_INFO si; //SYSTEM_INFO结构体包含了当前计算机的信息。这个信息包括计算机的体系结构、中央处理器的类型、系统中中央处理器的数量、页面的大小以及其他信息。
     GetSystemInfo(&si);
     for(unsigned long i = 0; i<si.dwNumberOfProcessors*2;i++)//dwNumberOfProcessors:指定系统中的处理器的数目
     {
         PostAccept();
     }
     //4.创建线程池
     for(unsigned long i = 0; i<si.dwNumberOfProcessors*2;i++){
         m_thread = (HANDLE)_beginthreadex(0,0,&ThreadProc,this,0,0);
         if(m_thread)
               m_lstThread.push_back(m_thread);
     }
     return true;
}

bool CTCPNet::PostAccept()
{
    //声明结构体对象
    Mysocket *pSocket = new Mysocket;
    //赋值
    pSocket->m_nType = NT_ACCEPT;
    pSocket->m_olp.hEvent = WSACreateEvent();
    pSocket->m_sock = socket(AF_INET,SOCK_STREAM,0);
    ZeroMemory(pSocket->m_szbuf,sizeof(pSocket->m_szbuf));

    DWORD dwBytesReceived;
    //投递接收连接请求
    if(!AcceptEx(m_sock,//监听socket
                 pSocket->m_sock,//waiter
                 pSocket->m_szbuf,//缓冲区
                 0,//连接成功,立即返回
                 sizeof (sockaddr)+16,//本地地址长度
                 sizeof (sockaddr)+16,//远程地址长度
                 &dwBytesReceived,//接收传输字节数
                 &pSocket->m_olp//事件
    ))
    {
        if (WSAGetLastError() != ERROR_IO_PENDING)//还没有处理完的情况不算是错误
        {
            closesocket(pSocket->m_sock);
            WSACloseEvent(pSocket->m_olp.hEvent);
            delete pSocket;
            pSocket = NULL;
            return false;
        }
    }
    m_lstMysocket.push_back(pSocket);
    return true;
}

bool CTCPNet::PostRecv(Mysocket* pSocket)
{
    pSocket->m_nType = NT_READ;
    WSABUF wb;
    wb.buf = pSocket->m_szbuf;
    wb.len = sizeof (pSocket->m_szbuf);
    DWORD dwNumberOfBytesRecvd;
    DWORD dwFlags = FALSE;
    if(WSARecv(pSocket->m_sock,//一个标识已连接套接口的描述字
               &wb,//一个指向WSABUF结构数组的指针。每一个WSABUF结构包含一个缓冲区的指针和缓冲区的长度
               1,//lpBuffers数组中WSABUF结构的数目。
               &dwNumberOfBytesRecvd,//如果接收操作立即结束,一个指向本调用所接收的字节数的指针
               &dwFlags,//一个指向标志位的指针
               &pSocket->m_olp,//一个指向WSAOVERLAPPED结构的指针
               NULL//一个指向接收操作结束后调用的例程的指针,在异步操作中可设置为NULL
               ))
    {
        if(WSAGetLastError() == WSA_IO_PENDING){
            return false;
        }
    }
    return true;
}

unsigned _stdcall CTCPNet::ThreadProc(void *lpvoid)
{
    CTCPNet *pthis =(CTCPNet*)lpvoid;
    //接收连接
    DWORD dwNumberOfBytesTransferred;
    SOCKET sock;
    Mysocket *pSocket;
    BOOL bFlag;//大BOOL类型 为int
    while(pthis->m_flagquit)
    {
        //观察IOCP状态
        bFlag=GetQueuedCompletionStatus(pthis->m_hIOCP,//观察哪一个完成端口
                                  &dwNumberOfBytesTransferred,//接收的字节数
                                  (PULONG_PTR)&sock,//完成键
                                  (LPOVERLAPPED *)&pSocket,//OVERLAPPED是包含事件的结构体  LPOVERLAPPED:指向结构体得指针 LPOVERLAPPED *:二级指针  当前跟客户端连接的指针
                                  INFINITE//等的时间
                    );
       if(!bFlag){
            //psocket 包含的就是下线的客户端对应的waiter
            auto ite = pthis->m_lstMysocket.begin();
            while (ite != pthis->m_lstMysocket.end())
            {
                if ((*ite)==pSocket)
                {
                    WSACloseEvent((*ite)->m_olp.hEvent);
                    closesocket((*ite)->m_sock);
                    delete *ite;
                    *ite = NULL;
                    pthis->m_lstMysocket.erase(ite);
                    break;
                }
                ite++;
            }
            continue;
        }
        //校验参数
        if (!sock || !pSocket)
            continue;

        switch(pSocket->m_nType){
        case NT_ACCEPT:
            //说明已经连接上了
               //1.投递新的空闲waiter
               pthis->PostAccept();
               //2.投递接收数据请求
               pthis->PostRecv(pSocket);
               //3.将socket交给IOCP管理
               CreateIoCompletionPort((HANDLE)pSocket->m_sock,//交给完成端口管理的套接字
                                      pthis->m_hIOCP,//完成端口句柄
                                      (ULONG_PTR)pSocket->m_sock,//完成键
                                      0);//线程的个数 默认和系统处理器一样的个数
            break;
        case NT_READ:
            //数据接收到了
            pSocket->m_szbuf;
            //处理数据
            pthis->PostRecv(pSocket);
            break;
        default:
            break;
        }
    }

    return 0;
}

unsigned _stdcall CTCPNet::ThreadRecv(void *lpvoid)
{
    //一个线程对应一个waiter 用map
    CTCPNet *pthis =(CTCPNet*)lpvoid;

    pthis->RecvData();

    return 0;
}

void CTCPNet::RecvData()
{
    //获取线程id映射sockWaiter
    SOCKET sockWaiter = m_mapIdToSock[GetCurrentThreadId()];
    int nPackSize;
    int nRecvNum;
    char* szbuf=NULL;
    int offset;
    while(m_flagquit)
    {
        nRecvNum = recv(sockWaiter,(char*)&nPackSize,sizeof (int),0);
        if (nRecvNum<0)
        {
            //客户端下线
            if (WSAGetLastError() == WSAECONNRESET)
            {
            break;
            }
            continue;
        }
        if (nPackSize<=0) continue;

        //解决粘包问题
        szbuf = new char[nPackSize];
        offset = 0;
        while(nPackSize)
        {
            nRecvNum = recv(sockWaiter,szbuf+offset,nPackSize,0);
            offset += nRecvNum;
            nPackSize -= nRecvNum;
        }
        //处理数据
        cout<<szbuf<<endl;
        delete []szbuf;
        szbuf = NULL;
    }
}

 bool CTCPNet::SendData(SOCKET sock,char *szbuf,int nlen)
{
     if(sock == INVALID_SOCKET || !szbuf || nlen<0)
         return false;
     //发送大小
     if(send(sock,(char*)&nlen,sizeof(int),0) <= 0)
         return false;
    //发送内容
     if(send(sock,szbuf,nlen,0)<=0)
         return false;
     return true;
}

void CTCPNet::UnInitNetWork()
{
    //销毁线程
    m_flagquit = false;
    list<HANDLE>::iterator ite = m_lstThread.begin();
    while(ite != m_lstThread.end()){
        if (*ite)
        {
            if (WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
            {
                   TerminateThread(*ite,-1);
            }
            CloseHandle(*ite);
            *ite = NULL;
        }
        ite++;
    }
    m_lstThread.clear();

    //m_lstMysocket链表删除
    auto itesocket = m_lstMysocket.begin();
    while (itesocket != m_lstMysocket.end())
    {
        closesocket((*itesocket)->m_sock);
        WSACloseEvent((*itesocket)->m_olp.hEvent);
        delete *itesocket;
        *itesocket = NULL;
        itesocket++;
    }
    m_lstMysocket.clear();

    //关闭套接字
    if(m_sock){
        closesocket(m_sock);
        m_sock = NULL;
    }
    WSACleanup();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值