【面试题】网络IO多路复用模型之异步事件

news/2024/7/7 1:21:27 标签: 网络, tcp/ip, 网络协议

目录

异步事件模型的概念

工作流程:

WSAEventSelect模型的优势和不足

代码:


异步事件模型的概念

WSAEventSelect模型是WindowsSockets提供的另外一个有用的异步I/O模型。该模型允许一个或多个套接字上接收以事件为基础的网络事件通知。Windows Sockets应用程序在创建套接字后,调用WSAEventSlect()函数,将一个事件对象与网络事件集合关联在一起。当网络事件发生时,应用程序以事件的形式接收网络事件通知。

工作流程:

1.通过WSAEventSelect()函数 向windows注册

2.监测事件什么时候有信号WSAWaitForMultipleEvents ()

3.判断发生的是什么网络事件WSAEnumNetworkEvents()

4.根据发生的事件进行相应的处理

WSAEventSelect模型的优势和不足

优势:可以在一个非窗口的Windows Sockets程序中,实现多个套接字的管理。性能较优

不足:

1.每个WSAEventSelect模型最多只能管理64个套接字。当应用程序中需要管理多于64个套接字时,就需要额外创建线程。

2.由于使用该模型开发套接字应用程序需要调用几个相关函数才能完成。因此,该模型增加了开发的难度,增加了开发人员的编码量。从这个角度讲,该模型不如WSAAysnceSelect模型方便。

代码:

TCPServer.h

#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>
#include <windows.h>
#include <map>
//#define MAXNUM  64
const int MAXNUM = 64;
 //socket----包大小、缓冲区、偏移量
struct SocketInfo{
    int nPackSize;
    char *pszbuf;
    int offset;
};
class TCPServer
{
public:
    TCPServer();
    ~TCPServer();
public:
    //1.初始化网络--加载、创建socket\bind\listen
    bool initNetWork(const char* szip = "127.0.0.1",unsigned short nport = 1234);
    void unInitNetWork(const char* szerr = "null"); //卸载网络
    bool sendData(SOCKET sockWaiter,const char* szbuf,int nlen); //向指定客户端发送数据
    void recvData(); //接收数据

    static DWORD WINAPI threadProc(LPVOID lpvoid);
private:
    SOCKET m_socklisten;
    std::list<HANDLE> m_lstThread;
    bool  m_bFlagQuit;
    std::map<DWORD,SOCKET> m_mapThreadIdToSocket;
    SOCKET m_arysocket[MAXNUM];
    HANDLE m_aryEvent[MAXNUM];
    int    m_nEventNum;
    std::map<SOCKET,SocketInfo*> m_mapSocketToInfo;
};

#endif // TCPSERVER_H

TCPServer.cpp

#include "tcpserver.h"

TCPServer::TCPServer()
{
    m_socklisten = 0;
    m_bFlagQuit = true;
    m_nEventNum = 0;
    ZeroMemory(m_aryEvent,sizeof(m_aryEvent));
    ZeroMemory(m_arysocket,sizeof(m_arysocket));
}

TCPServer::~TCPServer()
{

}

bool TCPServer::initNetWork(const char *szip, unsigned short nport)
{
    //1.选择种类  韩餐 火锅  串串香   川菜 -- 加载库
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        /* Tell the user that we could not find a usable */
        /* Winsock DLL.                                  */
        printf("WSAStartup failed with error: %d\n", err);
        return false;
    }

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        /* Tell the user that we could not find a usable */
        /* WinSock DLL.                                  */
        printf("Could not find a usable version of Winsock.dll\n");
        unInitNetWork();
        return false;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");
    //2.雇人-店长--
    m_socklisten = socket(AF_INET,SOCK_STREAM,0);
    if(m_socklisten == INVALID_SOCKET){
        unInitNetWork("socket err");
        return false;
    }
    //3.选择地址--
    sockaddr_in addrserver;
    addrserver.sin_family = AF_INET;
    addrserver.sin_addr.S_un.S_addr = inet_addr(szip);
    addrserver.sin_port = htons(nport);
    if( SOCKET_ERROR == bind(m_socklisten,(sockaddr*)&addrserver,sizeof(addrserver))){
        unInitNetWork("bind err");
        return false;
    }
    //4.宣传--
    if( SOCKET_ERROR == listen(m_socklisten,1000)){ //  1_1_1_1______
        unInitNetWork("listen err");
        return false;
    }
    HANDLE  hevent = WSACreateEvent(); //默认人工 匿名无信号事件
    if(0 ==WSAEventSelect(m_socklisten,hevent,FD_ACCEPT)){
        //注册成功
        m_arysocket[m_nEventNum] = m_socklisten;
        m_aryEvent[m_nEventNum] = hevent;
        ++m_nEventNum;
    }

//    //创建接收连接的线程
    HANDLE hThread = CreateThread(0,0,&threadProc,this,0,0);
    if(hThread)
        m_lstThread.push_back(hThread);
    return true;
}


DWORD TCPServer::threadProc(LPVOID lpvoid)
{
    TCPServer *pthis = (TCPServer*)lpvoid;
    DWORD dwIndex;
    WSANETWORKEVENTS wwe;
    sockaddr_in addrclient;
    int nsize = sizeof(addrclient);
    while(pthis->m_bFlagQuit){
         //等事件,判断哪个事件有信号
        dwIndex =WSAWaitForMultipleEvents(pthis->m_nEventNum, //事件的个数
                                 pthis->m_aryEvent, //监听事件的数组
                                 FALSE,//任意一个有信号就返回
                                 WSA_INFINITE,//等待时间
                                 0
                                 );
        dwIndex -=WSA_WAIT_EVENT_0;
         //判断发生什么事了
        if(WSAEnumNetworkEvents(pthis->m_arysocket[dwIndex],
                             pthis->m_aryEvent[dwIndex],
                                 &wwe))
            continue;
        // 处理
        if(wwe.lNetworkEvents & FD_ACCEPT){

            printf("wait client connect......\n");
            SOCKET sockWaiter = accept(pthis->m_socklisten,(sockaddr*)&addrclient,&nsize);
            printf("client ip:%s port:%d\n",inet_ntoa(addrclient.sin_addr),addrclient.sin_port );
            //向windows注册
            HANDLE  hEvent = WSACreateEvent();
            if( 0==WSAEventSelect(sockWaiter,hEvent,FD_READ|FD_CLOSE)){
                pthis->m_aryEvent[pthis->m_nEventNum] =hEvent;
                pthis->m_arysocket[pthis->m_nEventNum] = sockWaiter;
                ++pthis->m_nEventNum;
            }
        }
        if(wwe.lNetworkEvents & FD_READ){

            SocketInfo *p =   pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]];
            if(p == NULL){
                p = new SocketInfo;
                p->nPackSize =0;
                p->offset =0;
                p->pszbuf = NULL;
                 //接收包大小
                int nReadNum = recv(pthis->m_arysocket[dwIndex],(char*)&p->nPackSize,sizeof(int),0);
                if(nReadNum >0){
                    p->pszbuf = new char[p->nPackSize];

                }
                pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]] = p;

            }else{
                  //接收包数据
                int nReadNum = recv(pthis->m_arysocket[dwIndex],p->pszbuf+p->offset,p->nPackSize,0);
                if(nReadNum>0){
                    p->offset+=nReadNum;
                    p->nPackSize-=nReadNum;
                    if(p->nPackSize ==0){
                        //todo
                        printf("client say:%s\n",p->pszbuf);
                        delete []p->pszbuf;
                        pthis->m_mapSocketToInfo[pthis->m_arysocket[dwIndex]] = NULL;
                    }
                }

            }




        }

        if(wwe.lNetworkEvents & FD_CLOSE){

            closesocket(pthis->m_arysocket[dwIndex]);
            WSACloseEvent(pthis->m_aryEvent[dwIndex]);
            if(pthis->m_nEventNum >1){
                pthis->m_aryEvent[dwIndex] = pthis->m_aryEvent[pthis->m_nEventNum-1];
                pthis->m_arysocket[dwIndex] = pthis->m_arysocket[pthis->m_nEventNum-1];

            }
            --pthis->m_nEventNum;
        }

    }
    return 0;
}






void TCPServer::unInitNetWork(const char* szerr )
{
    printf(szerr);
    if(m_socklisten){
        closesocket(m_socklisten);
        m_socklisten = 0;
    }
    WSACleanup();
    //结束所有的线程
    m_bFlagQuit = false;
    auto ite = m_lstThread.begin();
    while(ite !=m_lstThread.end()){
        //判断线程状态
        if(WAIT_TIMEOUT == WaitForSingleObject(*ite,100))
            TerminateThread(*ite,-1);

        CloseHandle(*ite);
        *ite = NULL;
        ++ite;
    }

    m_lstThread.clear();
}

bool TCPServer::sendData(SOCKET sockWaiter, const char *szbuf, int nlen)
{
    //发送的包大小
    if(send(sockWaiter,(const char*)&nlen,sizeof(int),0) <=0)
        return false;
    //发送包内容
    if(send(sockWaiter,szbuf,nlen,0) <=0)
        return false;
    return true;
}






http://www.niftyadmin.cn/n/5534757.html

相关文章

UNIAPP_顶部导航栏右侧添加uni-icons图标,并绑定点击事件,自定义导航栏右侧图标

效果 1、导入插件 uni-icons插件&#xff1a;https://ext.dcloud.net.cn/plugin?nameuni-icons 复制 uniicons.ttf 文件到 static/fonts/ 下 仅需要那个uniicons.ttf文件&#xff0c;不引入插件、单独把那个文件下载到本地也是可以的 2、配置页面 "app-plus":…

中俄汽车产业链合作前景广阔,东方经济论坛助力双边合作与创新

随着中国汽车零部件企业的竞争力和创新能力不断增强&#xff0c;中国汽车及零部件行业在俄罗斯的市场份额和品牌影响力显著提升&#xff0c;中俄两国在汽车产业链上的合作展现出巨大的潜力和广阔的前景。2024年5月&#xff0c;俄罗斯乘用车新车销量达到12.8万辆&#xff0c;同比…

Elasticsearch环境搭建|ES单机|ES单节点模式启动|ES集群搭建|ES集群环境搭建

文章目录 版本选择单机ES安装与配置创建非root用户导入安装包安装包解压配置JDK环境变量配置single-node配置JVM参数后台启动|启动日志查看启动成功&#xff0c;访问终端访问浏览器访问 Kibana安装修改配置后台启动|启动日志查看浏览器访问 ES三节点集群搭建停止es服务域名配置…

SpringBoot设置自动跳转前端界面

一般情况下&#xff0c;我们的Application启动文件的内容为一行的运行代码&#xff0c;默认启动项目以后不会自动跳转到我们的前端页面 public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);} 这里我的可以通过设置文件的内容&#…

一文告诉你旅游类软文如何写更吸引人

又放暑假啦~仅限于除上班族以外的栋梁们&#xff01;面对这长达两个月的假期&#xff0c;总会听到有人感慨“世界这么大&#xff0c;我想出去看看”。生活和工作上的双重压力让大家总是期盼寻觅到释放之处&#xff0c;而旅游成为了人们心灵的一剂解药&#xff0c;相信不少人已经…

如何在 SCSS 中实现复杂的嵌套选择器并确保代码的可维护性?

在 SCSS 中实现复杂的嵌套选择器时&#xff0c;可以遵循以下几个原则以确保代码的可维护性&#xff1a; 限制嵌套层级&#xff1a;避免层级过深的嵌套&#xff0c;最好不要超过三级。过多的嵌套会增加代码的复杂性和选择器的特异性&#xff0c;降低代码的可读性和维护性。 使用…

ONLYOFFICE8.1版本桌面编辑器测评

官网链接&#xff1a;https://www.onlyoffice.com/zh/https://www.onlyoffice.com/zh/pdf-editor.aspx 官方下载链接&#xff1a;https://www.onlyoffice.com/zh/download-desktop.aspx 主要功能和特点&#xff1a; 综合办公套件&#xff1a;集成了文字处理、电子表格和演示文…

【elementui】记录解决el-tree开启show-checkbox后,勾选一个叶结点后会自动折叠的现象

第一种解决方案&#xff1a;设置default-expand-keys的值为当前选中的key值即可 <el-treeref"tree"class"checkboxSelect-wrap":data"treeData"show-checkboxnode-key"id":expand-on-click-node"true":props"defau…