事件选择模型


事件选择模型

前言

windows处理用户行为的两种方式

  • 消息机制

    • 核心:消息队列

      • 处理过程

        • 所有的用户操作,比如点鼠标,摁键盘,点软件上的按钮…等等,所有操作均依次按顺序被记录,装进一个队列
      • 特点

        • 消息队列由操作系统维护,咱们做的操作,然后把消息取出来,分类处理
        • 有先后顺序
      • 其他

        • 我们之后教大家win32,MFC课程,这部分都是基于这个消息队列,会给大家详细介绍,大家暂时从宏观理解
        • 我们下个模型,异步选择就是基于这个消息的
  • 事件机制

    • 核心:事件集合

      • 处理过程

        • 根据需求,我们为用户的特定操作绑定一个事件,事件由我们自己调用API创建,需要多少创建多少
        • 将事件投递给系统,系统就帮咱们监视着,所以不能无限创建,太多系统运行就卡了
        • 如果操作发生了,比如用户按鼠标了,那么对应的事件就会被置成有信号,也就是类似1变2了,用个数标记aaa
        • 我们直接获取到有信号的事件,然后处理
      • 特点

        • 所有事件都是咱们自己定义的,系统只是帮咱们置有无信号,所以我们自己掌管定义…
        • 无序的
      • 其他

        • 咱们这个事件选择,就是应用这个

事件选择的逻辑

整体逻辑跟select差不多,你看这个叫WSAEventSelect 名字上一看,就是select的进化版

第一步

  • 创建一个事件对象(变量)

    • WSACreateEvent

第二步

  • 为每一个事件对象绑定个socket以及操作accept,read,close…并投递给系统

    • 投递给系统,咱们就完全不用管了,系统自己监管

      • 咱们就去做别的事儿去了
    • WSAEventSelect

第三步

  • 查看事件是否有信号

    • WSAWaitForMultipleEvents

第四步

  • 有信号的话就分类处理

    • WSAEnumNetworkEvents

服务端

网络头文件 网络库

  • 最底层的网络函数,大家用QT MFC wpf,或者百度下载的很多其他的封装好的网络库,都是对咱们讲的这些最本质的网络函数的二次封装,咱们讲的是通用的,讲完这个大家也可以自己封装函数库给别人用了。

  • #include <WinSock2.h>
    #pragma comment(lib,“ws2_32.lib”)

    • 函数库

      • winsock2.h

        • windows socket 第2版

          • 第一版是 winsock.h
          • 查看具体区别
        • 目前网络库有哪些版本?

          • 1.0
            1.1
            2.0
            2.1
            2.2

            • 演示MSDN

              • 查这个函数的详细信息

                • WSAStartup
        • 我们的开发环境支持哪个版本?

          • 打开头文件,看咱们当前编译器环境支持的最高版本
      • ws2_32.lib

        • windows socket 第2版 32位

          • 不管是64编译环境还是32编译环境,都是用这个,并没有ws2_64.dll
      • wsock32.lib

        • winsock.h 第一版网络库 对应的库文件
      • 这里不区分大小写

打开网络库

  • int WSAStartup(
    WORD wVersionRequired,
    LPWSADATA lpWSAData
    );

    • 功能

      • 打开网络库/启动网络库,启动了这个库,这个库里的函数/功能才能使用

      • w windows
        s socket
        a Asynchronous 异步
        startup 启动

        • 同步与异步

          • 同步

            • 阻塞/卡死状态
          • 异步

            • 多个工作同时进行
    • 参数1

      • 我们要使用的库的版本

      • 类型是 WORD

        • 转定义:unsigned short
      • WORD wVersionRequired = MAKEWORD(2,2);

        • MAKEWORD(主版本,副版本), 1.0 2.2

        • wVersionRequired

          • 数据高位/高地址是副版本
          • 数据低位/低地址是主版本
          • 用调试+计算器演示数据原理
      • 位运算,内存操作,小端存储

    • 参数2

      • LPWSADATA lpWSAData

        • 系统通过这个参数给我们一些配置信息

          • 注意

            • 当看到参数有 LP P前缀的时候,是说我们这里要传对应类型变量的地址,这是win32API 的规范 或者叫规则
        • 看下有哪些信息

          • struct WSAData {
            WORD wVersion;
            WORD wHighVersion;
            unsigned short iMaxSockets;
            unsigned short iMaxUdpDg;
            char *lpVendorInfo;
            char szDescription[WSADESCRIPTION_LEN + 1];
            char szSystemStatus[WSASYS_STATUS_LEN + 1];
            }

            • wVersion

              • 我们要使用的版本
            • wHighVersion

              • 系统能提供给我们最高的版本
            • iMaxSockets

              • 返回可用的socket的数量,2版本之后就没用了
            • iMaxUdpDg

              • UDP数据报信息的大小,2版本之后就没用了
            • lpVendorInfo

              • 供应商特定的信息,2版本之后就没用了
            • szDescription
              szSystemStatus

              • 当前库的描述信息,2.0是第二版的意思
        • 当输入的版本不存在

          • 输入1.3, 2.3

            • 有主版本,没有副版本

              • 得到该主版本的最大副版本 1.1 2.2并使用
          • 输入3.1 3.3

            • 超过最大版本号

              • 使用系统能提供的最大的版本 2.2
          • 输入 0.0 0.1 0.3

            • 主版本是0

              • 网络库打开失败,不支持请求的套接字版本
    • 返回值

      • 返回0为执行正确

      • 失败

        • 这些宏的本质
        • WSASYSNOTREADY 10091 底层网络子系统尚未准备好进行网络通信。 系统配置问题,重启下电脑,检查ws2_32库是否存在,或者是否在环境配置目录下
          WSAVERNOTSUPPORTED 10092 此特定Windows套接字实现不提供所请求的Windows套接字支持的版本。 要使用的版本不支持
          WSAEPROCLIM 10067 已达到对Windows套接字实现支持的任务数量的限制。 Windows Sockets实现可能限制同时使用它的应用程序的数量
          WSAEINPROGRESS 10036 正在阻止Windows Sockets 1.1操作。 当前函数运行期间,由于某些原因造成阻塞,会返回在这个错误码,其他操作均禁止
          WSAEFAULT 10014 lpWSAData参数不是有效指针。 参数写错了

校验版本

  • 2 != HIBYTE(wsaData.wVersion) || 2 != LOBYTE(wsaData.wVersion)

    • HIBYTE是高位 副版本

    • LOBYTE是地位 主版本

    • 逻辑

      • 只要有一个不是2,说明系统不支持我们要的2.2版本
  • 关闭库
    并结束函数,可以给出相应提示

    • WSACleanup();
    • return 0;

创建SOCKET

  • SOCKET socket(
    int af,
    int type,
    int protocol
    );

    • 作用

      • 创建一个SOCKET
    • SOCKET介绍

      • 什么是socket

        • 将底层复杂的协议体系,执行流程,进行了封装,封装完的结果,就是一个SOCKET了,
        • 也就是说,SOCKET是我们调用协议进行通信的 操作接口
      • 意义

        • 将复杂的协议过程与我们编程人员分开,我们直接操作一个简单SOCKET就行了,对于底层的协议 过程细节,完全不用知道,这就大大方便了我们。

          • 网络编程难就难在协议本身的复杂性,简单就简单在我们编程层面完全不用考虑哪些
      • 本质

        • 就是一种数据类型,转定义看下类型

          • 就是一个整数

            • uint
          • 但是这个数是唯一的

            • 标识着我当前的应用程序,协议特点等信息
            • ID,门牌号
      • 应用

        • 我们网络通信的函数,全部都要使用SOCKET

          • 演示
        • 逻辑

          • 每个客户端有一个SOCKET,服务器有一个SOCKET,通信时候,就需要这个SOCKET做参数,给谁通信,就要传递谁的SOCKET
      • 所以

        • 网络编程,理论层面SOCKET是网络封装的精华,代码层面就是不停的使用SOCKET这个变量,所以又叫SOCKET编程
    • 参数1

      • 地址的类型

        • 比如大家联系我

          • 手机

            • 15512345678
          • 固定电话

            • 7881234
          • QQ

            • 40916626
          • 微信

            • c3_xin666
          • 找上门

            • 内蒙古 xxxxxxx
        • AF_INET 2

          • ipv4

            • Internet协议版本4(IPv4)地址系列。

              • 192.168.1.103

                • 0.0.0.0 ~ 255.255.255.255
                • 点分十进制表示法
            • 4字节 32位的地址

              • 个数快不够

                • 就是无符号int类型的范围 0 ~ 4294967295
        • AF_INET6 23

          • ipv6

            • Internet协议版本6(IPv6)地址系列。

              • 2001:0:3238:DFE1:63::FEFB
            • 16字节 128位的地址

              • 这个地球每寸一个IP
        • AF_BTH 32

          • 蓝牙地址系列。
            如果计算机安装了蓝牙适配器和驱动程序,则Windows XP SP2或更高版本支持此地址系列。

            • 6B:2D:BC:A9:8C:12
        • AF_IRDA 26

          • 红外数据协会(IrDA)地址系列。
            仅当计算机安装了红外端口和驱动程序时,才支持此地址系列。
        • 通信地址不仅仅只有IP地址

    • 参数2

      • 套接字类型

        • SOCK_STREAM 1

          • 一种套接字类型,提供带有OOB数据传输机制的顺序,可靠,双向,基于连接的字节流。 此套接字类型使用传输控制协议(TCP)作为Internet地址系列(AF_INET或AF_INET6)。
        • SOCK_DGRAM 2

          • 一种支持数据报的套接字类型,它是固定(通常很小)最大长度的无连接,不可靠的缓冲区。 此套接字类型使用用户数据报协议(UDP)作为Internet地址系列(AF_INET或AF_INET6)。
        • SOCK_RAW 3

          • 一种套接字类型,提供允许应用程序操作下一个上层协议头的原始套接字。 要操作IPv4标头,必须在套接字上设置IP_HDRINCL套接字选项。 要操作IPv6标头,必须在套接字上设置IPV6_HDRINCL套接字选项。
        • SOCK_RDM 4

          • 一种套接字类型,提供可靠的消息数据报。 这种类型的一个示例是Windows中的实用通用多播(PGM)多播协议实现,通常称为可靠多播节目。
            仅在安装了可靠多播协议时才支持此类型值。
        • SOCK_SEQPACKET 5

          • 一种套接字类型,提供基于数据报的伪流数据包。
    • 参数3

      • 协议的类型

        • IPPROTO_TCP

          • 传输控制协议(TCP)。 当af参数为AF_INET或AF_INET6且类型参数为SOCK_STREAM时,这是一个可能的值。
        • IPPROTO_UDP

          • 用户数据报协议(UDP)。 当af参数为AF_INET或AF_INET6且类型参数为SOCK_DGRAM时,这是一个可能的值。
        • IPPROTO_ICMP

          • Internet控制消息协议(ICMP)。 当af参数为AF_UNSPEC,AF_INET或AF_INET6且类型参数为SOCK_RAW或未指定时,这是一个可能的值。
        • IPPROTO_IGMP

          • Internet组管理协议(IGMP)。 当af参数为AF_UNSPEC,AF_INET或AF_INET6且类型参数为SOCK_RAW或未指定时,这是一个可能的值。
        • IPPROTO_RM

          • 用于可靠多播的PGM协议。 当af参数为AF_INET且类型参数为SOCK_RDM时,这是一个可能的值。 在针对Windows Vista及更高版本发布的Windows SDK上,此协议也称为IPPROTO_PGM。
            仅在安装了可靠多播协议时才支持此协议值。
        • 整理下

          • 通过参数3得到一个事儿,参数1 2 3三者是配套的,是一套参数,不是随便填的,即使用不同的协议,那要添加对应的那套参数。
          • 想要使用一个协议,咱们设备得支持才行,比如红外
          • 参数3中,有个可能这个词,所以说一般,参数3可以填写0,系统会自动帮我们选择协议类型
    • 返回值

      • 成功返回可用的socket

        • 不用了就一定要销毁套接字

          • closesocket(socketListen);
      • 失败返回INVALID_SOCKET

        • 关闭网络库

          • WSACleanup();
        • 可用WSAGetLasterror()返回错误码

绑定地址与端口

  • int bind(
    SOCKET s,
    const sockaddr *addr,
    int namelen
    );

    • 作用

      • 给我们的socket绑定端口号与具体地址

        • 地址

          • 找到咱们的电脑

            • 只有一个
        • 端口号

          • 找到我们机器上对应的软件,比如QQ,浏览器等等,都对应着自己的端口号

            • 多个
          • 每一种通信的端口号是唯一的

          • 同一个软件可能占用多个端口号

    • 参数1

      • 上一个函数创建了socket,绑定了协议信息(地址类型,套接字类型,协议类型),咱们bind函数就是绑定实质的地址,端口号
    • 参数2

      • 结构体

        • 地址类型
        • 装IP地址
        • 端口号
      • 结构体类型

        • sockaddr

        • 该参数使用方法

          • SOCKADDR_IN sockAddress;
            sockAddress.sin_family = AF_INET;
            sockAddress.sin_addr.s_addr = inet_addr(“127.0.0.1”);
            sockAddress.sin_port = 12345;
            (sockaddr*)&sockAddress强转添加到参数2上

            • 成员1

              • 跟socket函数参数1是一样的
            • 成员2

              • IP地址

                • 192.168.xxx.xxx

                  • 可以在控制台输入指令 ipconfig 就能看到了
                  • 或者在网络设置中,能找到这个地址
                  • 我就不演示了,我不想让你们发现我
                • 127.0.0.1

                  • 回送地址 本地回环地址 本地网络测试
            • 参数3

              • 端口号

                • 本质

                  • 就是一个整数

                    • 0~65535
                  • IP是我们机器的地址,端口就是我们具体的软件的通信口,一个软件可能会占用多个接口,比如一个软件可以聊天,可以下载,可以看视频…那么这些不同的通信内容,往往会有各自的协议,各自的端口。

                    • IP是公司地址,端口就是各个部门的地址了
                • 填写哪个值呢?

                  • 理论上只要这个范围0~65535都可以

                    • 实际

                      • 介于0~1023,为系统保留占用端口号

                        • 21端口分配给FTP(文件传输协议)服务
                          25端口分配给SMTP(简单邮件传输协议)服务
                          80端口分配给HTTP服务
                    • 所以

                      • 我们不能写这个范围的

                        • 我们的范围就是1024~65535

                          • 稍微大点儿,1万多
                        • 但是注意一点,端口号是唯一的,比如1234已经被别的软件占用了,那么你再使用1234这个端口号,那么就会绑定失败,因为已经呗占用了

                          • 给大家演示下
                        • 那大家如何查看自己要用的端口号有没有被占用呢?

                          • 打开运行cmd输入netstat -ano

                            • 查看被使用的所有端口
                          • netstat -aon|findstr “12345”

                            • 检查我们要使用的端口号是否被使用了
                            • 使用了就会显示使用的程序,未被使用就啥都不显示
    • 参数3

      • 参数2的类型大小

        • sizeof(参数2)
    • 返回值

      • 成功返回0

      • 失败返回SOCKET_ERROR

        • 具体错误码通过int WSAGetLastError(void);获得
        • closesocket(socketListen);
          WSACleanup();

开始监听

  • int WSAAPI listen(
    SOCKET s,
    int backlog
    );

    • 作用

      • 将套接字置于正在侦听传入连接的状态。
    • 参数1

      • 服务器端的socket,也就是socket函数创建的
    • 参数2

      • 挂起连接队列的最大长度。

        • 就是说,比如有100个用户链接请求,但是系统一次只能处理20个,那么剩下的80个不能不理人家,所以系统就创建个队列记录这些暂时不能处理,一会儿处理的链接请求,依先后顺序处理,那这个队列到底多大?就是这个参数设置,比如2,那么就允许两个新链接排队的。这个肯定不能无限大,那内存不够了。

        • 我们可以手动设置这个参数,但是别大了。可能210多,20多。

        • 我们一般填写这个参数

          • SOMAXCONN

            • 作用是让系统自动选择最合适的个数
            • 不同的系统环境不一样,所以这个合适的数也都不一样
    • WSAAPI

      • 调用约定

        • 这个我们可以忽略,这是给系统看的,跟咱们没关

        • 决定三

          • 函数名字的编译方式
          • 参数的入栈顺序
          • 函数的调用时间
    • 返回值

      • 成功

        • 返回0
      • 失败

        • SOCKET_ERROR

        • 具体错误码

          • WSAGetLastError()

          • 释放

            • closesocket(socketListen);
            • WSACleanup();

事件选择

  • 创建一个事件对象

    • WSAEVENT WSAAPI WSACreateEvent();

      • 成功

        • 返回一个事件
      • 失败

        • 返回WSA_INVALID_EVENT

        • WSAGetLastError获取错误码

        • 关闭

          • ::closesocket(socketServer);
            ::WSACleanup();
      • 转定义

        • HANDLE

          • 句柄

            • ID

            • 内核对象

              • 由系统在内核申请

              • 由操作系统访问

              • 我们不能定位其内容,也不能修改

                • void*

                  • 通用类型指针
                • 对内核的保护,对规则的保护,从而使操作系统有序的平稳的,有效的运行,而不会随便出问题

              • 调用函数创建,调用函数释放

                • 如果我们没有调用释放,那么他可能就一直存在于内核,造成内核内存泄漏, 这种只能重启电脑
              • 内核对象有哪些

                • socket
                • Kernel Objects
    • 几个函数

      • BOOL WSAAPI WSACloseEvent
        (
        WSAEVENT hEvent
        );

        • 关闭/释放事件句柄

          • 不用就要释放
      • BOOL WSAAPI WSAResetEvent
        (
        WSAEVENT hEvent
        );

        • 重置WSAEventSelect函数使用的事件对象状态的正确方法是将事件对象的句柄传递给hEventObject参数中的WSAEnumNetworkEvents函数。 这将重置事件对象并以原子方式调整套接字上活动FD事件的状态。
      • BOOL WSAAPI WSASetEvent
        (
        WSAEVENT hEvent
        );

        • 将指定事件主动置成有信号
  • 绑定并投递

    • int WSAAPI WSAEventSelect
      (
      SOCKET s,
      WSAEVENT hEventObject,
      long lNetworkEvents
      );

      • 功能

        • 给事件绑上socket与操作码,并投递给操作系统
      • 参数1

        • 被绑定的socket

          • 最终,每个socket都会被绑定一个事件
      • 参数2

        • 事件对象

          • 逻辑,就是讲参数1与参数2绑定在一起
      • 参数3

        • 具体事件

          • FD_ACCEPT

            • 有客户端链接

              • 与服务器socket绑定
          • FD_READ

            • 有客户端发来消息

              • 与客户端socket绑定
              • 可多个属性并列 用 |
          • FD_CLOSE

            • 客户端下线了

              • 与客户端socket绑定
              • 包含强制下线,正常下线
          • FD_WRITE

            • 可以给客户端发信

              • 与客户端socket绑定
              • 会在accept后立即主动产生该信号
              • 可以说明,客户端连接成功,即可随时send
            • 有特点

              • 代码写完给大家测试
          • FD_CONNECT

            • 客户端一方,给服务器绑定这个
          • 0

            • 取消事件监视

              • WSAEventSelect(FD_ACCEPT | FD_READ);
              • WSAEventSelect(0);
          • FD_OOB

            • 带外数据

              • 不多说了,一般不使用
          • FD_QOS

            • 套接字服务质量状态发生变化消息通知

              • 比如:当网络发生拥堵时:用户下载,看电影,聊天,听歌…好多用网事件一起在做,那么计算机网速是有限的,每秒可以处理多少数据,这时候,计算机就会把要紧事优先,比如可以降低下载的速度,以保证看电影流畅,这时候,下载的服务质量就发生了变化。如果投放了这个事件,就会接收到信号了
              • 通过这些小问题的介绍,大家就要注意到实际应用中的问题了,稍微记一下
            • WSAIoctl

              • 得到服务质量信息
              • char strOut[2048] = { 0 };
                DWORD nLen = 2048;
                WSAIoctl(socketServer, SIO_QOS, 0, 0, strOut, nLen, &nLen, NULL, NULL);
          • FD_GROUP_QOS

            • 保留

              • 还没有对其赋值具体意义,还没用呢
            • 想要接收套接字组QoS更改的通知。

          • 重叠I/O模型中

            • FD_ROUTING_ INTERFACE_CHANGE

              • 想要接收指定目标的路由接口更改通知。

              • 数据到达对方的所经过的线路改变了,由于是动态优化选择

              • 要通过此函数WSAIoctl注册之后,才可以

                • SIO_ROUTING_ INTERFACE_CHANGE
            • FD_ADDRESS_ LIST_CHANGE

              • 想要接收套接字地址族的本地地址列表更改通知。

                • 咱们服务器,链接了很多客户端,那服务器就记录着所有的客户端的地址信息,也就是相当于一个列表,当多一个或者少一个,就是变化了, 咱们就能得到相关的信号了
              • 要通过此函数WSAIoctl注册之后,才可以有效

                • SIO_ADDRESS_ LIST_CHANGE
      • 返回值

        • 成功

          • 返回0
        • 失败

          • 失败SOCKET_ERROR

            • WSACloseEvent(wsaEvent);
              closesocket(socketServer);
              WSACleanup();
  • 询问事件

    • DWORD WSAAPI WSAWaitForMultipleEvents(
      DWORD cEvents,
      const WSAEVENT *lphEvents,
      BOOL fWaitAll,
      DWORD dwTimeout,
      BOOL fAlertable
      );

      • 作用

        • 获取发生信号的事件
      • 参数1

        • 事件个数

          • 定义事件列表(数组)个数是

            • WSA_MAXIMUM_WAIT_EVENTS

              • 64个
              • 该函数参数1最大64
            • 可以变大

              • 方法我们在逻辑讲完了再讲,有一点儿复杂
              • 不像select模型,直接就能变大,因为select本身就是个数组,然后遍历就行了,比较直接,事件选择是异步投放,由系统管理,咱们就不能随便修改了,要按照规则来
      • 参数2

        • 事件列表
      • 参数3

        • 事件等待方式

          • TRUE

            • 所有的事件都产生信号,才返回
          • FALSE

            • 任何一个事件产生信号,立即返回
            • 返回值减去WSA_WAIT_EVENT_0表示事件对象的索引,其状态导致函数返回。
            • 如果在调用期间发出多个事件对象的信号,则这是信号事件对象的数组索引,其中所有信号事件对象的索引值最小。
      • 参数4

        • 超时间隔,以毫秒为单位。 跟select参数5一样的意义

        • 123

          • 等待123毫秒

            • 超时返回WSA_WAIT_TIMEOUT
        • 0

          • 检查事件对象的状态并立即返回。不管有没有信号
        • WSA_INFINITE

          • 等待直到事件发生
      • 参数5

        • TRUE

          • 重叠IO模型使用
        • FALSE

          • 咱们这个事件选择模型填写FALSE
      • 返回值

        • 数组下标的运算值

          • 参数3为true

            • 所有的事件均有信号
          • 参数3为false

            • 返回值减去WSA_WAIT_EVENT_0==数组中事件的下标
        • WSA_WAIT_IO_COMPLETION

          • 参数5为TRUE,才会返回这个值
        • WSA_WAIT_TIMEOUT

          • 超时了,continue即可
        • WSA_WAIT_FAILED

          • 出错了
  • 列举事件

    • int WSAAPI WSAEnumNetworkEvents(
      SOCKET s,
      WSAEVENT hEventObject,
      LPWSANETWORKEVENTS lpNetworkEvents
      );

      • 获取事件类型,并将事件上的信号重置

        • accept recv close等等
      • 参数1

        • 对应的socket
      • 参数2

        • 对应的事件
      • 参数3

        • 触发的事件类型在这里装着

        • 是一个结构体指针

          • struct _WSANETWORKEVENTS
            {
            long lNetworkEvents;
            int iErrorCode[FD_MAX_EVENTS];
            }

            • 成员1:具体操作

              • 一个信号可能包含两个消息,以按位或的形式存在
            • 参数2:错误码数组

              • FD_ACCEPT事件错误码在FD_ACCEPT_BIT下标里
              • 没有错误,对应的就是0
      • 返回值

        • 成功

          • 0
        • 失败

          • SOCKET_ERROR
          • WSAGetLastError()得到错误码
  • 事件分类处理逻辑

    • if (lpNetworkEvents->lNetworkEvents & FD_ACCEPT)
      {
      if (lpNetworkEvents->iErrorCode[FD_ACCEPT_BIT] == 0)
      {
      //接受链接
      //创建事件
      //投放事件
      //元素增加
      }
      }

    • 用switch可以嘛?

      • 不行,有大bug
    • else if可以嘛?

      • 不太行,有小bug

有序处理

  • for (i = Index; i < EventTotal; i++)
    {
    Index = WSAWaitForMultipleEvents(1, &EventArray[i], TRUE, 1000, FALSE);
    if ((Index != WSA_WAIT_FAILED) && (Index != WSA_WAIT_TIMEOUT))
    {
    WSAEnumNetworkEvents(SocketArray[i], EventArray[i], &NetworkEvents);
    //分类处理
    }
    }

  • 最开始就一个一个检测

    • 不绝对的公平,只是相对公平,相对有序

      • 让大家在一轮循环下都能得到处理
      • 但是并不能完全解决顺序问题,只是达到相对公平
      • 所以,事件选择模型不能用于大用户,多访问

增加事件数量

  • 一个一个来,一个大数组就行了啊

    • 结合线程池
  • 一组一组来

    • 单线程,一组一组顺序处理就好了

    • 创建多个线程,每个线程处理一个事件表,最大64

      • 提醒

释放

对比select的结构吐