事件选择模型
前言
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)地址系列。
仅当计算机安装了红外端口和驱动程序时,才支持此地址系列。
- 红外数据协会(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)多播协议实现,通常称为可靠多播节目。
仅在安装了可靠多播协议时才支持此类型值。
- 一种套接字类型,提供可靠的消息数据报。 这种类型的一个示例是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。
仅在安装了可靠多播协议时才支持此协议值。
- 用于可靠多播的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地址
-
- 可以在控制台输入指令 ipconfig 就能看到了
- 或者在网络设置中,能找到这个地址
- 我就不演示了,我不想让你们发现我
-
127.0.0.1
- 回送地址 本地回环地址 本地网络测试
-
-
-
参数3
-
端口号
-
本质
-
就是一个整数
- 0~65535
-
IP是我们机器的地址,端口就是我们具体的软件的通信口,一个软件可能会占用多个接口,比如一个软件可以聊天,可以下载,可以看视频…那么这些不同的通信内容,往往会有各自的协议,各自的端口。
- IP是公司地址,端口就是各个部门的地址了
-
-
填写哪个值呢?
-
理论上只要这个范围0~65535都可以
-
实际
-
介于0~1023,为系统保留占用端口号
- 21端口分配给FTP(文件传输协议)服务
25端口分配给SMTP(简单邮件传输协议)服务
80端口分配给HTTP服务
- 21端口分配给FTP(文件传输协议)服务
-
-
所以
-
我们不能写这个范围的
-
我们的范围就是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();
- ::closesocket(socketServer);
-
-
转定义
-
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();
- WSACloseEvent(wsaEvent);
-
-
-
-
-
询问事件
-
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
- 提醒
-