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

目录

异步事件模型的概念

工作流程:

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.mfbz.cn/a/769894.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【STM32】在标准库中使用DMA

1.MDA简介 DMA全称Direct Memory Access,直接存储区访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作&#xff0c;传输动作本身是由DMA控制器来实现和完成的。DMA传输方式无需CPU直接控制传输&#xff0c;也没有中断处理方式那样保留现场和…

seq2seq+Attention机制原理介绍

一、Seq2seq的局限性 Seq2seq&#xff08;序列到序列&#xff09;模型我们在前面讲了它的原理&#xff0c;是一种广泛用于处理序列转换任务的深度学习架构&#xff0c;特别是在机器翻译、文本摘要、对话生成等应用中。然而&#xff0c;尽管seq2seq模型在某些领域取得了显著的成…

使用 Python2.7 抓取 systrace 文件

为了排查安卓终端系统底层问题&#xff0c;需要抓取终端的systrace文件分析&#xff0c;下面是操作步骤&#xff1a; 1、安装python 2.7 2、打开cmd执行命令安装python包&#xff1a;pip install pypiwin32 3、解压six-1.16.0.tar.gz&#xff0c;进入目录用命令安装six&#xf…

《UDS协议从入门到精通》系列——图解0x84:安全数据传输

《UDS协议从入门到精通》系列——图解0x84&#xff1a;安全数据传输 一、简介二、数据包格式2.1 服务请求格式2.2 服务响应格式2.2.1 肯定响应2.2.2 否定响应 Tip&#x1f4cc;&#xff1a;本文描述中但凡涉及到其他UDS服务的&#xff0c;均提供专栏内文章链接跳转方式以便快速…

Stable Diffusion最强功能—— 图片背景完美替换

今天分享 Stable Diffusion 图片背景完美替换 功能&#xff0c;通过 Stable Diffusion 图生图重绘蒙版进行背景图的二次重绘。 在广告产品图、头像背景替换、图片后期处理等场景下用到的都很频繁。 整体步骤&#xff1a; 通过 removebg 插件实现图片主体蒙版的抠图 结合图生…

提升研发效能的67个技术点丨IDCF

在当今快速变化的市场环境中&#xff0c;企业要想保持竞争力&#xff0c;就必须不断提高研发效率。高效的研发不仅能够帮助企业快速响应市场需求&#xff0c;还能降低成本、提高产品质量。本文让我们一起来看一下&#xff0c;作为微软18年MVP的技术大咖徐磊老师&#xff0c;梳理…

HTML CSS 基础复习笔记 - 列表使用

用于自己复习 自定义列表 示例代码 <!DOCTYPE html> <html> <head><title>Definition List Example</title> </head> <body><h1>古诗</h1><dl><dt>静夜思</dt><dd>床前明月光&#xff0c;疑…

使用dot来画流程图

Dot是一种图形描述语言&#xff0c;属于Graphviz软件的一部分。Graphviz是一个用于可视化图形&#xff08;图表、网络图等&#xff09;的开源工具集。使用Dot语言&#xff0c;你可以创建并描述节点和边&#xff0c;从而生成图形。以下是如何使用Dot语言画图的基本步骤&#xff…

修复 OpenSSH 爆出极其严重的安全漏洞!

最近几天OpenSSH爆出了一个高危漏洞&#xff1a;CVE-2024-6387&#xff0c;影响到了很多的Linux服务器系统。明月第一时间给所有的代维客户服务器进行了排查和漏洞修复&#xff0c;因此耽搁了一些时间。直到今天才算抽出空来给大家分享一下。严格上来说这个漏洞的危险性还是极高…

Beyond Compare 解锁版下载及安装教程 (文件和文件夹比较工具)

前言 Beyond Compare 是一款功能强大的文件和文件夹比较工具。它支持文件夹比较、文件夹合并与同步、文本比较、表格比较、图片比较、16进制比较、注册表比较、版本比较等多种功能。通过 Beyond Compare&#xff0c;您可以轻松调查文件和文件夹之间的不同之处&#xff0c;并使…

MySQL篇-SQL优化实战-减少子查询

回顾 上一篇了解了分析SQL使用的explain&#xff0c;可以点击查看MySQL篇-SQL优化实战了解我在写sql的注意事项还有explain的说明&#xff0c;这次拿一段生产使用的sql进行优化说明。从14s优化到2.6s 待优化的SQL SELECT DISTINCTswpe.tag_number,hca.ACCOUNT_NAME customer…

ELFK简介

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

K8S学习教程(二):在 PetaExpress KubeSphere容器平台部署高可用 Redis 集群

前言 Redis 是在开发过程中经常用到的缓存中间件&#xff0c;为了考虑在生产环境中稳定性和高可用&#xff0c;Redis通常采用集群模式的部署方式。 在制定Redis集群的部署策略时&#xff0c;常规部署在虚拟机上的方式配置繁琐并且需要手动重启节点&#xff0c;相较之下&#…

java基础:方法

一、方法 1、Java方法是语句的集合&#xff0c;它们在一起执行一个功能。 方法是解决一类问题的步骤的有序集合方法包含于类或对象中方法在程序中被创建&#xff0c;在其他地方被引用 2、设计方法的原则&#xff1a;方法的本意是功能块&#xff0c;就是实现某个功能的语句块…

layui+jsp项目中实现table单元格嵌入下拉选择框功能,下拉选择框可手动输入内容或选择默认值,修改后数据正常回显。

需求 table列表中的数据实现下拉框修改数据&#xff0c;当默认的下拉框不符合要求时&#xff0c;可手动输入内容保存。内容修改后表格显示修改后的值同时表格不刷新。 实现 layui框架下拉框组件只能选择存在的数据&#xff0c;不支持将输入的内容显示在input中的功能&#x…

什么牌子的无线领夹麦克风好,一篇了解哪种领夹麦性价比高

随着5G技术的广泛应用&#xff0c;短视频平台迎来了前所未有的发展机遇&#xff0c;几乎每个地方都有人在记录生活&#xff0c;分享故事。在这样的背景下&#xff0c;户外直播和视频创作的需求急剧增长&#xff0c;然而&#xff0c;户外的复杂声场仅靠普通手机的录音功能实在难…

计算机网络之局域网

目录 1.局域网的基本概念 2.LAN的特性 3.局域网特点 4.拓扑结构 5.传输媒体的选择 6.传输媒体 7.传输技术 8.传输技术距离问题 9.LAN的逻辑结构 10.局域网工作原理 上篇文章内容&#xff1a;OSI七层体系结构 1.局域网的基本概念 局域网 是将分散在有限地 理范围内&…

Robust Test-Time Adaptation in Dynamic Scenarios--论文阅读

论文笔记 资料 1.代码地址 https://github.com/BIT-DA/RoTTA 2.论文地址 https://arxiv.org/abs/2303.13899 3.数据集地址 coming soon 1论文摘要的翻译 测试时间自适应(TTA)旨在使预先7训练的模型适用于仅具有未标记测试数据流的测试分布。大多数以前的TTA方法已经在…

SQL Server特性

一、创建表 在sql server中使用create table来创建新表。 create table Customers( id int primary key identity(1,1), name varchar(5) ) 该表名为Customers其中包含了2个字段&#xff0c;分别为id&#xff08;主键&#xff09;以及name。 1、数据类型 整数类型&#xff…

NAT地址转换实验,实验超简单

实验拓扑 实验目的 将内网区域&#xff08;灰色区域&#xff09;的地址转换为172.16.1.0 实验过程 配置静态NAT&#xff08;基于接口的静态NAT&#xff09; R1配置 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysname R1 [R1]un in en I…
最新文章