网络IO模型之异步事件模型

该博客介绍了如何在Windows环境下使用CTCPNet类实现TCP网络的异步事件模型。通过WSAStartup初始化Winsock,创建监听套接字,并使用WSAEventSelect向操作系统注册网络事件。当事件发生时,使用WSAWaitForMultipleEvents和WSAEnumNetworkEvents处理接受连接、读取数据和关闭套接字等操作,实现了非阻塞的网络通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异步事件模型:
像操作系统注册信息(socket、网络事件、事件),当指定网络时间按发生,将事件置为信号。
特点:被动通知
个数限制 64 (多线程 多数组)
性能较优
阻塞:自己拷贝数据
如果有两个事件发生信号,只会发生索引最小的那个。

#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;
     }
     //向windows注册
     HANDLE hEvent = WSACreateEvent();//默认人工 无信号
     if (!WSAEventSelect(m_sock,hEvent,FD_ACCEPT|FD_CLOSE))
     {
         //SUCCESS
         m_aryhEvent[m_nEventNum] = hEvent;
         m_arySocket[m_nEventNum] = m_sock;
         m_nEventNum++;
     }
     m_thread = (HANDLE)_beginthreadex(0,0,&ThreadProc,this,0,0);
     if(m_thread)
     {
         m_lstThread.push_back(m_thread);
     }
     return true;
}

unsigned _stdcall CTCPNet::ThreadProc(void *lpvoid)
{
    CTCPNet *pthis =(CTCPNet*)lpvoid;
    DWORD dwIndex;
    WSANETWORKEVENTS we;
    char szbuf[1024]={0};
    //接收连接
    while(pthis->m_flagquit)
    {
        //等多个事件--有可能是ED_ACCEPT FD_READ FD_CLOSE
        dwIndex =WSAWaitForMultipleEvents(pthis->m_nEventNum,//等多少个事件
                                 pthis->m_aryhEvent,//事件数组 数组元素的类型为WSAEVENT类型
                                 FALSE,//等一个事件,还是等所有的事件,如果只需等众多事件中的一个到来,就开始工作,那么就设为FALSE,
                                 WSA_INFINITE,//等待的时间,如果是无限时等待,则把它设为:WSA_INFINITE.
                                 0);
        dwIndex -= WSA_WAIT_EVENT_0;//代表数组的下标 最小的索引的值
        //判断发生什么事
        if (WSAEnumNetworkEvents(pthis->m_arySocket[dwIndex],
                                 pthis->m_aryhEvent[dwIndex],
                                 &we))
            continue;
        //01 10 11
        if (we.lNetworkEvents & FD_ACCEPT)
        {
            //阻塞
            SOCKET sockWaiter = accept(pthis->m_sock,0,0);
            if (sockWaiter == INVALID_SOCKET){
                continue;
            }
                //异步事件向windows注册
                HANDLE hEvent = WSACreateEvent();//默认人工 无信号
                if (!WSAEventSelect(sockWaiter,hEvent,FD_READ|FD_CLOSE))
                {
                    pthis->m_aryhEvent[pthis->m_nEventNum] = hEvent;
                    pthis->m_arySocket[pthis->m_nEventNum] = sockWaiter;
                    pthis->m_nEventNum++;
                    //success

                }
        }

        //读操作:直接
        if (we.lNetworkEvents & FD_READ)
        {
            int nRecvNum = recv(pthis->m_arySocket[dwIndex],szbuf,sizeof(szbuf),0);
            if (nRecvNum > 0)
            {
                cout<<szbuf<<endl;
            }
        }

        //某个事件关闭,将数组中最后一个覆盖给关闭的那个
        //          1.关闭套接字
        //          2.关闭事件
        //          3.将数组中最后一个覆盖给关闭的事件和套接字
        //          4.数目--
        if (we.lNetworkEvents & FD_CLOSE)
        {
            //关闭套接字
            closesocket(pthis->m_arySocket[dwIndex]);
            //关闭事件
            WSACloseEvent(pthis->m_aryhEvent[dwIndex]);
            if (pthis->m_nEventNum>1)
            {
                pthis->m_arySocket[dwIndex] = pthis->m_arySocket[pthis->m_nEventNum];
                pthis->m_aryhEvent[dwIndex] = pthis->m_aryhEvent[pthis->m_nEventNum];
            }
            pthis->m_nEventNum--;
        }
    }
    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();
    //关闭套接字
    if(m_sock){
        closesocket(m_sock);
        m_sock = NULL;
    }
    WSACleanup();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值