博客原文地址


WSAEventSelect模型是类似于WSAAsyncSelect模型的另一个有用的异步I/O模型。它允许应用程序在一个或者多个套接字上接收以事件为基础的网络事件。 在这里,最主要的差别是在于网络事件会投递到一个事件对象句柄。并不是投递到一个窗口。

我们使用事件模型前,我们的应用程序针对使用的每一个套接字首先要创建一个事件对象:

[cpp] view plain copy
  1. WSAEVENT WSACreateEvent(void);

下面是注册自己感兴趣的网络事件类型:

[cpp] view plain copy
  1. int WSAEventSelect(
  2. _In_  SOCKET s,   //代表感兴趣的套接字
  3. _In_  WSAEVENT hEventObject,//指定要与套接字关联在一起的事件对象。用WSACreateEvent取得。
  4. _In_  long lNetworkEvents );//对应一个  位掩码   用于指定应用程序感兴趣的各种网络事件类型的一个组合。

当我们用WSACreateEvent创建了事件,他有两种工作状态和两种工作模式:

1、工作状态:已传信signaled 和 未传信nonsignaled。

2、工作模式:人工重置manual reset  和  自动重置auto reset。

刚开始的时候,wsacreateevent是一种未传信的工作状态,并且是人工重置的模式来创建一个句柄。   但是我们网络事件触发了与一个套接字关联在一起的事件对象,工作状态便会从“未传信”转变为“已传信”,由于事件对象是 一种人工重置模式创建的,所以在完成了I/O请求处理后,我们的应用程序需要负责将工作状态改变为“未传信”。   要这样我们需要一个函数:

[cpp] view plain copy
  1. BOOL WSAResetEvent(   //设置为未传信状态
  2. __in          WSAEVENT hEvent
  3. );

上面这个函数唯一的参数是一个事件句柄,成功返回TRUE,失败返回FALSE;

当我们完成一个事件对象的处理后,我们要调用WSACloseEvent函数,释放由事件句柄使用的系统资源:

[cpp] view plain copy
  1. BOOL WSACloseEvent(
  2. __in          WSAEVENT hEvent
  3. );

这个函数也是只有一个参数:事件句柄。 成功返回TRUE,失败返回FALSE;

当我们的一个套接字和一个事件句柄关联在一起后,应用程序就开始I/O处理,通过WSAWaitForMultipleEvents函数等待网络事件来出发事件对象句柄的工作状态。(这个函数用来等待一个或者多个事件对象句柄,并在事先指定的一个或所有句柄进入“已传信”状态后,并在超过一个规定的事件周期后立即返回)

[cpp] view plain copy
  1. DWORD WSAWaitForMultipleEvents(
  2. __in          DWORD cEvents,//和下面的参数一起顶一个了由WSAEVENT对象构成的一个数组,数组中cEvents指定的是事件对象的数量,lphEvents对应的是一个指针,用于直接引用该数组
  3. __in          const WSAEVENT* lphEvents,
  4. __in          BOOL fWaitAll,//指定了这个函数如何等待在事件数组中的对象
  5. __in          DWORD dwTimeout,//规定了这个函数最多可等待一个网络事件发生有多少时间,毫秒为单位,超时规定的事件会立即返回函数。
  6. __in          BOOL fAlertable//在用WSAEventSelect模型的时候,可以忽略它,要设为FALSE。 这个参数主要用于在重叠I/O模型中。
  7. );

来看一个第三个参数fWaitAll。  如果这个参数你设为TRUE,那么直有等待lphEvents数组内包含的所有事件对象都进入“已传信”状态函数才返回。   如果你设为FALSE,那么任何一个事件对象进入“已传信”状态,函数就会返回。

通常应用程序应将这个参数设为FALSE,一次只为一个套接字事件提供服务。

第四个参数dwTimeout要注意的是尽量避免将超时值设为0, 加入没有等待处理的事件,WSAWaitForMultipleEvents便会返回WSA_WAIT_TIMEOUT。

你将这个参数设为:WSAINFINITE(永久等待),那么只有当在一个网络事件传信了一个事件对象后函数才会返回。

当WSAWaitForMultipleEvents 收到一个事件对象的网络事件通知,便会返回一个值。指出造成函数返回的事件对象。  我们的应用程序就可以引用事件数组中已传信的事件,并检索与那个事件对应的套接字,判断到底是在那个套接字上,发生了什么网络事件类型。对于事件数组中的事件进行引用时,应该用WSAWaitForMultipleEvents的返回值 减去预定义的WSA_WAIT_EVENT_0得到具体的引用值:

[cpp] view plain copy
  1. Index = WSAWaitForMultipleEvents(...);
  2. MyEvent = EventArray[Index - WSA_WAIT_EVENT_0];

当我们知道了造成网络事件的套接字以后,下面可以调用WSAEnumNetworkEvents函数,调查发生了什么类型的网络事件:

[cpp] view plain copy
  1. int WSAEnumNetworkEvents(
  2. __in          SOCKET s,//对应于造成网络时间的套接字
  3. __in          WSAEVENT hEventObject,//可选参数,制定了一个事件句柄,对应于打算重设的那个事件对象。  我们的事件对象本来是"已传信“状态,你将它传入,令其自动成为”未传信“状态。   如果你不想用这个参数来重设事件,你可以用函数WSAResetEvent。
  4. __out         LPWSANETWORKEVENTS lpNetworkEvents//一个指针,指向WSANETWORKEVENTS结构。接收套接字上发生的网络事件类型以及可能出现的任何错误代码。
  5. );
[cpp] view plain copy
  1. typedef struct _WSANETWORKEVENTS {
  2. long lNetworkEvents; //指定一个值,对应于套接字上发生的所有网络事件类型。
  3. int iErrorCode[FD_MAX_EVENTS];//制定一个错误代码数组。与lNetworkEvents中的事件关联在一起,对每个网络事件类型都存在着一个特殊的事件索引,于事件类型的名字类似,只要在事件名字后面添加一个”_BIT"后缀字串就好。
  4. } WSANETWORKEVENTS,  *LPWSANETWORKEVENTS;

看下面的实例:

[cpp] view plain copy
  1. if (NetwordEvents.lNetworkEvents & FD_READ)
  2. {
  3. if (NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
  4. {
  5. printf("FD_READ failed with error %d\n", NetworkEvents.iErrorCode[FD_READ_BIT]);
  6. }
  7. }

当完成了对WSANETWORKEVENTS结构中的事件的处理之后,我们的应用程序应在所有可用的套接字上继续等待更多的网络事件。

下面来看一下使用步骤:

1、建立一个socket。

2、建立一个event。

3、用WSAEventSelect将socket,和event关联起来。lNetworkEvents可以为 FD_ACCEPT ,FD_READ ,FD_WRITE,FD_CLOSE 等等.

4、等待事件句柄:index = WSAWaitForMultipleEvents(EventTotal,EventArray,FAlSE,100/*WSA_INFINITE*/,FALSE);

5、查询网络事件: int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject,LPWSANETWORKEVENTS lpNetworkEvents)

6、用lpNetworkEvents.lNetworkEvents & FD_ACCEPT ,lpNetworkEvents.lNetworkEvents & FD_CLOSE找到各个事件处理。

[cpp] view plain copy
  1. #include <WinSock2.h>
  2. #include <iostream>
  3. using namespace std;
  4. #pragma comment(lib, "WS2_32.lib")
  5. int main()
  6. {
  7. WSAEVENT eventArray[1024];
  8. SOCKET sockArray[1024];
  9. int nEventTotal = 0;
  10. WSADATA wsadata;
  11. WORD sockVersion = MAKEWORD(2, 0);
  12. WSAStartup(sockVersion, &wsadata);
  13. SOCKET s  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  14. sockaddr_in sin;
  15. sin.sin_family = AF_INET;
  16. sin.sin_port = htons(8888);
  17. sin.sin_addr.S_un.S_addr = INADDR_ANY;
  18. if (bind(s, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
  19. {
  20. cout << "error" << endl;
  21. return 0;
  22. }
  23. listen(s, 5);
  24. WSAEVENT event = ::WSACreateEvent();
  25. ::WSAEventSelect(s, event, FD_ACCEPT|FD_CLOSE);
  26. eventArray[nEventTotal] = event;
  27. sockArray[nEventTotal] = s;
  28. while(TRUE)
  29. {
  30. // 在所有事件对象上等待
  31. int nIndex = ::WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);
  32. // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
  33. nIndex = nIndex - WSA_WAIT_EVENT_0;
  34. for(int i=nIndex; i<nEventTotal; i++)
  35. {
  36. nIndex = ::WSAWaitForMultipleEvents(1, &eventArray[i], TRUE, 1000, FALSE);
  37. if(nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
  38. {
  39. continue;
  40. }
  41. else
  42. {
  43. // 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
  44. WSANETWORKEVENTS event;
  45. ::WSAEnumNetworkEvents(sockArray[i], eventArray[i], &event);
  46. if(event.lNetworkEvents & FD_ACCEPT)                // 处理FD_ACCEPT通知消息
  47. {
  48. if(event.iErrorCode[FD_ACCEPT_BIT] == 0)
  49. {
  50. if(nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
  51. {
  52. cout << " Too many connections! " << endl;
  53. continue;
  54. }
  55. SOCKET sNew = accept(sockArray[i], NULL, NULL);
  56. WSAEVENT event = ::WSACreateEvent();
  57. ::WSAEventSelect(sNew, event, FD_READ|FD_CLOSE|FD_WRITE);
  58. // 添加到表中
  59. eventArray[nEventTotal] = event;
  60. sockArray[nEventTotal] = sNew;
  61. nEventTotal++;
  62. }
  63. }
  64. else if(event.lNetworkEvents & FD_READ)            // 处理FD_READ通知消息
  65. {
  66. if(event.iErrorCode[FD_READ_BIT] == 0)
  67. {
  68. char szText[256];
  69. int nRecv = ::recv(sockArray[i], szText, 256, 0);
  70. if(nRecv > 0)
  71. {
  72. szText[nRecv] = 0;
  73. cout << "接收到数据:" << szText << endl;
  74. }
  75. }
  76. }
  77. else if(event.lNetworkEvents & FD_CLOSE)        // 处理FD_CLOSE通知消息
  78. {
  79. if(event.iErrorCode[FD_CLOSE_BIT] == 0)
  80. {
  81. ::closesocket(sockArray[i]);
  82. for(int j=i; j<nEventTotal-1; j++)
  83. {
  84. eventArray[j] = eventArray[j+1];
  85. sockArray[j] = sockArray[j+1];
  86. }
  87. nEventTotal--;
  88. i--;
  89. }
  90. }
  91. else if(event.lNetworkEvents & FD_WRITE)        // 处理FD_WRITE通知消息
  92. {
  93. }
  94. }
  95. }
  96. }
  97. return 0;
  98. }

事件选择WSAEventSelect相关推荐

  1. 【网络编程】之九、事件选择WSAEventSelect

    WSAEventSelect模型是类似于WSAAsyncSelect模型的另一个有用的异步I/O模型.它允许应用程序在一个或者多个套接字上接收以事件为基础的网络事件. 在这里,最主要的差别是在于网络事 ...

  2. TCP/IP 事件选择模型

    服务端: 1.打开网络库 int WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData); 2.校验版本号 2 != HIBYTE(wsaData ...

  3. 恋与实习生服务器维护,恋与制作人设计实习生事件选什么好?恋与制作人设计实习生事件选择推荐...

    恋与制作人设计实习生事件是众多事件之一,那么恋与制作人设计实习生事件到底选什么好啊?如何才能顺利过关呢?接下来就由小编给大家带来相关的信息,希望对大家有所帮助. 恋与制作人设计实习生事件选什么好?恋与 ...

  4. php 单选框选中事件,html中的checkbox和radio事件选择用法详解

    radio注册了click事件以后,神奇的是用键盘上的上下左右选择时,居然会触发鼠标事件,滚轮也会触发,这种神奇的事情在mousedown下面是不会发生的.(webkit不能使用上下左右选择) che ...

  5. select模型使用例子

    在windows平台构建网络应用,必须了解socket I/O模型.windows提供了选择(select).异步选择(WSAAsyncSelect).事件选择(WSAEventSelect).重叠I ...

  6. [问题解决] socket 10053

    前几天初步解决了困扰许久的socket10053的问题. 现象如下:windows下开发多线程socket通信的系统(采用事件选择WSAEventSelect的方式),在高并发的情况下send及rec ...

  7. socket10053

    前几天初步解决了困扰许久的socket10053的问题. 现象如下:windows下开发多线程socket通信的系统(采用事件选择WSAEventSelect的方式),在高并发的情况下send及rec ...

  8. WinSock三种选择I/O模型

    在<套接字socket及C/S通信的基本概念>和<WinSock编程基础>中,我们介绍了套接字的基本概念和WinSock API的基本调用规范.我们讨论了阻塞模式/非阻塞模式和 ...

  9. Winsock异步模式I/O模型WSAEventSelect的使用

    1.Winsock同步阻塞方式的问题 在异步非阻塞模式下,像accept(WSAAccept),recv(recv,WSARecv,WSARecvFrom)等这样的winsock函数调用后马上返回,而 ...

最新文章

  1. 一种解决 MacBook 里的 App Store 无法登录的问题
  2. 【Android 应用开发】Paint 滤镜原理 之 图像结构 ( 图片文件二进制分析 | PNG文件结构 | 数据块结构 | IHDR 数据块详解 )
  3. php 函数静态变量,php 函数中静态变量使用的问题实例分析
  4. html下拉折叠菜单,原生Js_实现简单的下拉折叠菜单(添加弹出动画效果)
  5. java反射设置属性值_Java反射如何有效的修改final属性值详解
  6. [USACO] Beef McNuggets
  7. 高性能MySQL(4)——查询性能优化
  8. 后台长期运行进程的三种方式
  9. scikit-learn中随机森林使用详解
  10. 电信5g网络apn接入点_华为就5G网络设备禁令起诉瑞典邮政和电信管理局
  11. 基于JAVA+SpringMVC+Mybatis+MYSQL的村民信息管理系统
  12. JVM参数之-XX:+HeapDumpOnOutOfMemoryError(导出内存溢出的堆信息(hprof文件))
  13. c语言数组字节偏移,C语言数组中的地址偏移问题
  14. java设置登录超时时间设置_session超时时间设置方法
  15. PHP接收云之家审批结果,首页云之家开放平台文档
  16. 蓝桥杯单片机学习记录——LED灯闪烁
  17. Solr---string类型的docValues属性、stored属性
  18. yigo项目中使用的函数
  19. thinkphp6-学习记录-应用手册
  20. 计算机硬件日语,求一些电脑硬件的日语说法(比如显卡等)

热门文章

  1. 2021辽宁高考成绩查询公布,2021辽宁高考成绩什么时候出
  2. sql基于聚合结果集取最大值_SQL超入门第三篇:写给产品、运营、分析师的SQL教程...
  3. 解决Dataframe删除操作时警告:SettingWithCopyWarning:A value is trying to be set on a copy of a slice from a...
  4. Gensim加载word2vec模型与简易使用
  5. pandas颠倒dataframe与series的顺序
  6. 逆波兰计算器android源码简书,汪都能理解的逆波兰计算器(C++实现)
  7. 怎么用计算机搜索文件,Win7系统如何使用内置搜索功能筛选文件名与内容
  8. oracle test传入参数,oracle存储过程,test(测试)时传自定义类型参数问题
  9. 浅谈密码学中数论基础
  10. 2019西电网安实验班选拔考试