事件选择模型 windows处理用户行为的两种方式 消息机制 核心 :消息队列
处理过程 :所有的用户操作,比如点鼠标,按键盘,对软件进行的各种操作……等等,所有操作均依次按顺序被记录,装进一个队列。不同的操作对应着不同的编号。
特点 :消息队列由操作系统维护,用户进行操作,然后把消息读取出来,分类处理。有先后顺序 。动态的。
异步选择模型就是基于这个消息的 。
事件机制 核心 :事件集合
处理过程 :根据需求我们为用户的特定操作绑定一个事件,事件由我们自己调用API创建,需要多少创建多少。
将时间投递给系统,系统就帮我们监视着,所以不能无限创建,太多了系统运行就卡了。
如果操作发生了,比如用户按鼠标了,那么对应的事件就会被置成有信号,也就是类似1变2,用个数来标记。
直接获取到有信号的时间进行处理。
特点 :所有时间都是咱们自己定义的,系统只是帮咱们标记有无信号。无序 。
事件选择模型 ,就是应用这个。
事件选择 整体类似于select
创建一个事件对象(变量),WSACreateEvent
为每一个事件对象绑定socket以及操作accept,read,close…..
投递给系统让其监管,WSAEventSelect
查看事件是否有信号,WSAWaitForMultipleEvents
有信号就分类处理,WSAEnumNetworkEvents
1 2 3 4 5 6 7 8 网路头文件 网络库 打开网络库 校验版本 创建SOCKET 绑定地址与端口 开始监听 事件选择
创建一个事件对象 1 WSAEVENT WSAAPI WSACreateEvent () ;
例 :
1 WSAEVENT eventServer = WSACreateEvent();
1 2 3 4 5 6 7 8 9 成功-返回一个事件 失败-返回WSA_INVALID_EVENT(无效的事件对象) if (eventServer == WSA_INVALID_EVENT) { int a = WSAGetLastError(); closesocket(socketServer); WSACleanup(); return 0 ; }
就是定义了一个事件类型。
HANDLE(void *通用类型指针),句柄,句柄的本质是ID,内核对象,唯一的标识符。
内核对象 :内核对象是系统提供的用户模式下代码与内核模式下代码进行交互的基本接口(百度百科)。
当调用一个用于创建内核对象的函数时,该函数就返回一个用于标识该对象的** 句柄 **
1 2 3 4 5 6 7 8 9 10 11 内核对象 由系统在内核申请 由操作系统访问 我们不能定位其内容,也不能修改 void * 通用类型指针 对内核的保护,对规则的保护,从而使操作系统有序的平稳的, 有效的运行,而不会随便出问题 调用函数创建,调用函数释放 如果我们没有调用释放,那么他可能就一直存在于内核, 造成内核内存泄漏, 这种只能重启电脑 内核对象有哪些 socket Kernel Objects
关闭/释放事件句柄 不用就要释放
1 2 3 4 BOOL WSAAPI WSACloseEvent ( WSAEVENT hEvent );
例 :
1 WSACloseEvent(eventServer);
指定事件主动置成无信号的 1 2 3 4 BOOL WSAAPI WSAResetEvent ( WSAEVENT hEvent );
指定事件主动置成有信号的 1 2 3 4 BOOL WSAAPI WSASetEvent ( WSAEVENT hEvent );
绑定并投递 1 2 3 4 5 int WSAAPI WSAEventSelect ( SOCKET s, WSAEVENT hEventObject, long INetworkEvents ) ;
功能 给事件绑上socket与操作码,并投递给操作系统。
参数1 被绑定的socket,最终每个socket都会被绑定一个事件
参数2 事件对象,逻辑,就是将参数1和参数2绑定在一起
参数3 具体事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 FD_ACCEPT 有客户端链接,与服务器socket绑定 FD_READ 有客户端发来消息,与客户端socket绑定,可多个属性并列使用。 FD_CLOSE 客户端下线,与客户端绑定,包含强制下线 FD_WRITE 可以给客户端发信,与客户端socket绑定,会在accept之后立即主动产生该信号。可以说明,客户端连接成功。即可随时send FD_CONNECT 用在客户端上,给服务器绑定这个。 0 取消事件绑定。 FD_OOB 带外数据,一般不使用。 FD_QOS 套接字服务质量状态发生变化。动态变化。 FD_GROUP_QOS 保留——还没有对其赋值具体意义。
1 2 3 4 重叠I/O模型中 FD_ROUTING_ INTERFACE_CHANGE FD_ADDRESS_ LIST_CHANGE
返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 成功——返回0 失败——返回SOCKET_ERROR if (SOCKET_ERROR == WSAEventSelect(socketServer, SetEvent, FD_ACCEPT);) { int a = WSAGetLastError(); WSACloseEvent(eventServer); closesocket(socketServer); WSACleanup(); return 0 ; }
询问事件 1 2 3 4 5 6 7 DWORD WSAAPI WSAWaitForMultipleEvents ( DWORD cEvents, const WSAEVENT *lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable ) ;
功能 获取发生信号的事件。
参数1 事件个数,定义事件列表(数组)个数。
1 最大64 WSA_MAXIMUM_WAIT_EVENTS
可以变大,不像select模型,直接就能变大,因为select模型本身就是个数组,直接遍历即可, 比较直接,而事件选择是按照异步来投放,由系统进行管理,咱们就不能随便修改了,要按照规则来。
参数2 事件列表。
参数3 事件等待方式。
1 2 3 4 5 6 TRUE 所有事件都产生信号,才返回。 FALSE 任何一个事件产生信号,立即返回。 返回值减去WSA_WAIT_EVENT_0表示事件对象的索引,其状态导致函数返回。 如果在调用期间发出多个事件对象的信号,则这是信号事件对象的数组索引,其中所有信号事件对象的索引值最小。
参数4 超时间隔,以毫秒为单位。与select参数5意义相同。
1 2 3 123 等待123 秒,超时返回WSA_WAIT_TIMEOUT0 检查事件对象的状态并立即返回。不管有没有信号 WSA_INFINITE 等待,直到事件发生。
参数5
返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 数组下标的运算值,参数3 为TRUE 所有时间均有信号 参数3 位FALSE 返回值减去WSA_WAIT_EVENT_0==数 组中事件的下标 WSA_WAIT_IO_COMPLETION 参数5 为TRUE,才会返回这个值 WSA_WAIT_TIMEOUT 超时了,continue 即可。 while (1 ) { DWORD nRes = WSAWaitForMultipleEvents(esSet.count, esSet.eventall, FALSE,WSA_INFINITE, FALSE); if (nRes == WSA_WAIT_FAILED) { printf ("错误码%d\n" , WSAGetLastError()); break ; } DWORD nIndex = nRes - WSA_WAIT_EVENT_0; }
列举事件 1 2 3 4 5 int WSAAPI WSAEnumNetworkEvents ( SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents ) ;
获取事件类型,并将事件上的信号重置,accept,recv,close等
例 :
1 2 3 4 5 6 7 WSANETWORKEVENTS NetworkEvents; if (SOCKET_ERROR==WSAEnumNetworkEvents(esSet.sockall[nIndex], esSet.eventall[nIndex], &NetworkEvents)){ int a = WSAGetLastError(); printf ("错误码:%d\n" , a); break ; }
参数1 对应的socket
参数2 对应的事件
参数3 触发的事件类型在这里装着。是一个结构体指针。
1 2 3 4 5 6 7 8 struct _WSANETWORKEVENTS { long lNetworkEvents; int iErrorCode[FD_MAX_EVENTS]; }
返回值 1 2 成功——返回0 失败——返回SOCKET_ERROR
事件分类处理逻辑 1 2 3 4 5 6 7 8 9 10 if (lpNetworkEvents->lNetworkEvents & FD_ACCEPT){ if (lpNetworkEvents->iErrorCode[FD_ACCEPT_BIT] == 0 ) { } }
1 2 switch 不行有大bugelse if 不太行,有小bug
有序处理 优化
1 2 3 4 5 6 7 8 9 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个,由WSAWaitForMultipleEvents这个函数决定。
1 2 3 增加事件数量————一个一个投,一个大数组就行了, ————一组一组投,单线程,一组一组顺序处理就好了。 创建多线程,每个线程处理一个事件表,最大是64
完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 #define _CRT_SECURE_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <Winsock2.h> #include <string.h> #pragma comment(lib, "Ws2_32.lib" ) struct fd_es_set { unsigned short count; SOCKET sockall[WSA_MAXIMUM_WAIT_EVENTS]; WSAEVENT evnetall[WSA_MAXIMUM_WAIT_EVENTS]; }; struct fd_es_set esSet ;BOOL WINAPI fun (DWORD dwCtrlType) { switch (dwCtrlType) { case CTRL_CLOSE_EVENT: for (int i = 0 ; i < esSet.count; i++) { closesocket(esSet.sockall[i]); WSACloseEvent(esSet.evnetall[i]); } break ; } return TRUE; } int main (void ) { SetConsoleCtrlHandler(fun, TRUE); WORD wdVersion = MAKEWORD(2 , 2 ); WSADATA wdScokMsg; int nRes = WSAStartup(wdVersion, &wdScokMsg); if (0 != nRes) { switch (nRes) { case WSASYSNOTREADY: printf ("重启下电脑试试,或者检查网络库" ); break ; case WSAVERNOTSUPPORTED: printf ("请更新网络库" ); break ; case WSAEINPROGRESS: printf ("请重新启动" ); break ; case WSAEPROCLIM: printf ("请尝试关掉不必要的软件,以为当前网络运行提供充足资源" ); break ; } return 0 ; } if (2 != HIBYTE(wdScokMsg.wVersion) || 2 != LOBYTE(wdScokMsg.wVersion)) { WSACleanup(); return 0 ; } SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == socketServer) { int a = WSAGetLastError(); WSACleanup(); return 0 ; } struct sockaddr_in si ; si.sin_family = AF_INET; si.sin_port = htons(12345 ); si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1" ); if (SOCKET_ERROR == bind(socketServer, (const struct sockaddr*)&si, sizeof (si))) { int a = WSAGetLastError(); closesocket(socketServer); WSACleanup(); return 0 ; } if (SOCKET_ERROR == listen(socketServer, SOMAXCONN)) { int a = WSAGetLastError(); closesocket(socketServer); WSACleanup(); return 0 ; } WSAEVENT eventServer = WSACreateEvent(); if (WSA_INVALID_EVENT == eventServer) { int a = WSAGetLastError(); closesocket(socketServer); WSACleanup(); return 0 ; } if (SOCKET_ERROR == WSAEventSelect(socketServer, eventServer, FD_ACCEPT)) { int a = WSAGetLastError(); WSACloseEvent(eventServer); closesocket(socketServer); WSACleanup(); return 0 ; } esSet.evnetall[esSet.count] = eventServer; esSet.sockall[esSet.count] = socketServer; esSet.count++; while (1 ) { DWORD nRes = WSAWaitForMultipleEvents(esSet.count, esSet.evnetall, FALSE, WSA_INFINITE, FALSE); if (WSA_WAIT_FAILED == nRes) { int a = WSAGetLastError(); printf ("错误码:%d\n" , a); break ; } DWORD nIndex = nRes - WSA_WAIT_EVENT_0; WSANETWORKEVENTS NetworkEvents; if (SOCKET_ERROR == WSAEnumNetworkEvents(esSet.sockall[nIndex], esSet.evnetall[nIndex], &NetworkEvents)) { int a = WSAGetLastError(); printf ("错误码:%d\n" , a); break ; } if (NetworkEvents.lNetworkEvents & FD_ACCEPT) { if (0 == NetworkEvents.iErrorCode[FD_ACCEPT_BIT]) { SOCKET socketClient = accept(esSet.sockall[nIndex], NULL , NULL ); if (INVALID_SOCKET == socketClient) { continue ; } WSAEVENT wsaClientEvent = WSACreateEvent(); if (WSA_INVALID_EVENT == wsaClientEvent) { closesocket(socketClient); continue ; } if (SOCKET_ERROR == WSAEventSelect(socketClient, wsaClientEvent, FD_READ | FD_CLOSE | FD_WRITE)) { closesocket(socketClient); WSACloseEvent(wsaClientEvent); continue ; } esSet.sockall[esSet.count] = socketClient; esSet.evnetall[esSet.count] = wsaClientEvent; esSet.count++; printf ("accept event\n" ); } else { continue ; } } if (NetworkEvents.lNetworkEvents & FD_WRITE) { if (0 == NetworkEvents.iErrorCode[FD_WRITE_BIT]) { if (SOCKET_ERROR == send(esSet.sockall[nIndex], "connect success" , strlen ("connect success" ), 0 )) { int a = WSAGetLastError(); printf ("send faild, error code:%d\n" , a); continue ; } printf ("write event\n" ); } else { printf ("socket error code:%d\n" , NetworkEvents.iErrorCode[FD_WRITE_BIT]); continue ; } } if (NetworkEvents.lNetworkEvents & FD_READ) { if (0 == NetworkEvents.iErrorCode[FD_READ_BIT]) { char strRecv[1500 ] = { 0 }; if (SOCKET_ERROR == recv(esSet.sockall[nIndex], strRecv, 1499 , 0 )) { int a = WSAGetLastError(); printf ("recv faild, error code:%d\n" , a); continue ; } printf ("recv data: %s\n" , strRecv); } else { printf ("socket error code:%d\n" , NetworkEvents.iErrorCode[FD_READ_BIT]); continue ; } } if (NetworkEvents.lNetworkEvents & FD_CLOSE) { printf ("client close\n" ); printf ("client force out: %d\n" , NetworkEvents.iErrorCode[FD_CLOSE_BIT]); closesocket(esSet.sockall[nIndex]); esSet.sockall[nIndex] = esSet.sockall[esSet.count - 1 ]; WSACloseEvent(esSet.evnetall[nIndex]); esSet.evnetall[nIndex] = esSet.evnetall[esSet.count - 1 ]; esSet.count--; } } for (int i = 0 ; i < esSet.count; i++) { closesocket(esSet.sockall[i]); WSACloseEvent(esSet.evnetall[i]); } WSACleanup(); system("pause" ); return 0 ; }
对比select模型 事件选择模型——异步
select模型——同步