事件选择模型概述

Winsock提供了另一种有用的异步事件通知I/O模型——WSAEventSelect模型。这个模型与WSAAsyncSelect模型类似,允许应用程序在一个或者多个套接字上接收基于事件的网络通知。它与 WSAAsyncSelect模型类似是因为它也接收FDXXX类型的网络事件,不过并不是依靠Windows的消息驱动机制,而是经由事件对象句柄通知

API详解

  1. WSAEVENT WSAAPI WSACreateEvent();

返回值

如果未发生错误, WSACreateEvent 将返回事件对象的句柄。 否则,返回值WSA_INVALID_EVENT。

作用

创建新的事件对象

  1. int WSAAPI WSAEventSelect(
  2. SOCKET s, //标识套接字的描述符。
  3. WSAEVENT hEventObject, //标识要与指定FD_XXX网络事件集关联的事件对象的句柄。
  4. long lNetworkEvents//一个位掩码,指定应用程序感兴趣的FD_XXX网络事件的组合。
  5. );

返回值

如果应用程序的网络事件的规范和关联的事件对象成功,则返回值为零。 否则,返回值SOCKET_ERROR。

作用

给事件绑上socket与操作码,并投递给操作系统,应用程序便可以在事件上等待了。

事件类型 含义
FD_READ 应用程序想接收是否有可读的通知
FD_WRITE 应用程序想接收是否有可写的通知
FD_OOB 应用程序想接收是否有OOB数据抵达通知
FD_ACCEPT 应用程序想接收与传入连接有关的通知
FD_CONNECT 应用程序想接收一个已完成连接的通知或者一个多点join操作的通知
FD_CLOSE 应用程序想接收与套接字关闭有关的通知
  1. DWORD WSAAPI WSAWaitForMultipleEvents(
  2. DWORD cEvents,
  3. const WSAEVENT *lphEvents,
  4. BOOL fWaitAll,
  5. DWORD dwTimeout,
  6. BOOL fAlertable
  7. );

cEvents

lphEvents 指向的数组中的事件对象句柄数。 事件对象句柄的最大数目 是WSA_MAXIMUM_WAIT_EVENTS。 必须指定一个或多个事件。

lphEvents

指向事件对象句柄数组的指针。 数组可以包含不同类型的对象的句柄。 如果 fWaitAll 参数设置为 TRUE,则它可能不包含同一句柄的多个副本。 如果在等待仍在挂起时关闭其中一个句柄,则未定义 WSAWaitForMultipleEvents 的行为。

fWaitAll

一个指定等待类型的值。 如果为 TRUE,则当 发出 lphEvents 数组中所有对象的状态时,函数将返回。 如果为 FALSE,则函数在发出任何事件对象的信号时返回。 在后一种情况下,返回值减 去WSA_WAIT_EVENT_0 指示导致函数返回其状态的事件对象的索引。 如果在调用期间发出了多个事件对象的信号,则这是信号事件对象的数组索引,其索引值为所有信号事件对象的最小索引值。

dwTimeout

超时间隔(以毫秒为单位)。 WSAWaitForMultipleEvents 如果超时间隔过期,即使 不满足 fWaitAll 参数指定的条件,也会返回。 如果 dwTimeout 参数为零, WSAWaitForMultipleEvents 将测试指定事件对象的状态并立即返回。 如果 dwTimeoutWSA_INFINITE, WSAWaitForMultipleEvents 将永远等待;也就是说,超时间隔永远不会过期。

fAlertable

一个值,该值指定线程是否处于可警报的等待状态,以便系统可以执行I/O完成例程。 如果为TRUE,则线程处于可警报的等待状态,当系统执行 I/O 完成例程时, WSAWaitForMultipleEvents 可以返回。 在这种情况下,将返回 WSA_WAIT_IO_COMPLETION ,并且等待的事件尚未发出信号。 应用程序必须再次调用 WSAWaitForMultipleEvents 函数。 如果为FALSE,则线程不会处于可警报的等待状态,并且不会执行 I/O 完成例程。

  1. int WSAAPI WSAEnumNetworkEvents(
  2. SOCKET s, //标识套接字
  3. WSAEVENT hEventObject, //用于标识要重置的关联事件对象的可选句柄
  4. LPWSANETWORKEVENTS lpNetworkEvents //指向 WSANETWORKEVENTS 结构的指针,该结构填充了发生的网络事件记录和任何关联的错误代码
  5. );

返回值

如果操作成功,则返回值为零。 否则,返回值SOCKET_ERROR

作用

枚举出与事件对象相关联的套接字发生了哪些信号,结果放在WSANETWORKEVENTS结构体中

工作原理

流程大致是这样:

  1. 定义一个socket数组和event数组
  2. 每一个socket操作关联一个event对象
  3. 调用WSAWaitForMultipleEvents函数等待事件的触发
  4. 调用WSAEnumNetworkEvents函数查看是哪个一个事件,根据事件找到相应的socket,然后进行相应的处理:比如数据显示等,同时,记得要将那个event重置为无信号状态。
  5. 循环步骤3和4,直到服务器退出。

    流程图

代码实现

服务端

  1. UINT CMFCWSAEventDlg::ThreadProc(LPVOID lparam) {
  2. // TODO: 在此添加控件通知处理程序代码
  3. CMFCWSAEventDlg* p = (CMFCWSAEventDlg*)lparam;
  4. SocketInit socketInit;
  5. SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  6. if (socketServer == INVALID_SOCKET) {
  7. AfxMessageBox(_T("套接字创建失败"));
  8. closesocket(socketServer);
  9. WSACleanup();
  10. }
  11. sockaddr_in sock;
  12. sock.sin_family = AF_INET;
  13. sock.sin_port = htons(5678);
  14. sock.sin_addr.S_un.S_addr = INADDR_ANY;
  15. int n = sizeof(sock);
  16. if (bind(socketServer, (sockaddr*)&sock, sizeof(sock)) == SOCKET_ERROR) {
  17. AfxMessageBox(_T("监听失败"));
  18. closesocket(socketServer);
  19. WSACleanup();
  20. }
  21. if (listen(socketServer, SOMAXCONN) == SOCKET_ERROR) {
  22. AfxMessageBox(_T("监听失败"));
  23. closesocket(socketServer);
  24. WSACleanup();
  25. }
  26. p->showText.SetWindowText("开始监听\r\n");
  27. // 创建事件对象,并关联到新的套节字
  28. WSAEVENT event = ::WSACreateEvent();
  29. ::WSAEventSelect(socketServer, event, FD_ACCEPT | FD_CLOSE);
  30. // 添加到表中
  31. p->eventArray[p->nEventTotal] = event;
  32. p->sockArray[p->nEventTotal] = socketServer;
  33. p->nEventTotal++;
  34. CString str;
  35. sockaddr_in addrRemote;
  36. while (1){
  37. // 在所有事件对象上等待
  38. int nIndex = ::WSAWaitForMultipleEvents(p->nEventTotal, p->eventArray, FALSE, WSA_INFINITE, FALSE);
  39. // 对每个事件调用WSAWaitForMultipleEvents函数,以便确定它的状态
  40. nIndex = nIndex - WSA_WAIT_EVENT_0;
  41. for (int i = nIndex; i < p->nEventTotal; i++)
  42. {
  43. nIndex = ::WSAWaitForMultipleEvents(1, &p->eventArray[i], TRUE, 1000, FALSE);
  44. if (nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT)
  45. {
  46. continue;
  47. }
  48. else
  49. {
  50. // 获取到来的通知消息,WSAEnumNetworkEvents函数会自动重置受信事件
  51. WSANETWORKEVENTS event;
  52. ::WSAEnumNetworkEvents(p->sockArray[i], p->eventArray[i], &event);
  53. if (event.lNetworkEvents & FD_ACCEPT) // 处理FD_ACCEPT通知消息
  54. {
  55. if (event.iErrorCode[FD_ACCEPT_BIT] == 0)
  56. {
  57. if (p->nEventTotal > WSA_MAXIMUM_WAIT_EVENTS)
  58. {
  59. p->showText.SetSel(-1);
  60. p->showText.ReplaceSel("时间太长");
  61. continue;
  62. }
  63. int nAddrLen = sizeof(addrRemote);
  64. SOCKET sNew = ::accept(p->sockArray[i], (SOCKADDR*)&addrRemote, &nAddrLen);
  65. //MessageBox("已连接");
  66. int nLen = p->showText.GetWindowTextLengthA();
  67. //p->showText.SetWindowText()
  68. str.Format("%s建立连接\r\n", ::inet_ntoa(addrRemote.sin_addr));
  69. p->showText.SetSel(-1);
  70. p->showText.ReplaceSel(str);
  71. WSAEVENT event = ::WSACreateEvent();
  72. ::WSAEventSelect(sNew, event, FD_READ | FD_CLOSE | FD_WRITE);
  73. // 添加到表中
  74. p->eventArray[p->nEventTotal] = event;
  75. p->sockArray[p->nEventTotal] = sNew;
  76. p->nEventTotal++;
  77. }
  78. }
  79. else if (event.lNetworkEvents & FD_READ) // 处理FD_READ通知消息
  80. {
  81. if (event.iErrorCode[FD_READ_BIT] == 0)
  82. {
  83. //char szText[256];
  84. char szText[1024] = { 0 };
  85. //memset(szText, 0, sizeof(szText));
  86. int nlen = strlen(szText);
  87. int nRecv = ::recv(p->sockArray[i], szText,1024, 0);
  88. //AfxMessageBox(nRecv);
  89. if (nRecv > 0)
  90. {
  91. szText[nRecv] = '\0';
  92. str.Format("%s发来了一条消息:%s\r\n", ::inet_ntoa(addrRemote.sin_addr), szText);
  93. p->showText.SetSel(-1);
  94. p->showText.ReplaceSel(str);
  95. //szText[0] = '\0';
  96. // 向客户端发送数据
  97. char *sendText = getallprime(1000);
  98. if (::send(p->sockArray[i], sendText, strlen(sendText), 0) > 0)
  99. {
  100. p->showText.SetSel(-1);
  101. p->showText.ReplaceSel("已发送结果\r\n");
  102. }
  103. }
  104. }
  105. }
  106. else if (event.lNetworkEvents & FD_CLOSE) // 处理FD_CLOSE通知消息
  107. {
  108. if (event.iErrorCode[FD_CLOSE_BIT] == 0)
  109. {
  110. ::closesocket(p->sockArray[i]);
  111. for (int j = i; j < p->nEventTotal - 1; j++)
  112. {
  113. p->eventArray[j] = p->eventArray[j + 1];
  114. p->sockArray[j] = p->sockArray[j + 1];
  115. }
  116. p->nEventTotal--;
  117. }
  118. p->showText.SetSel(-1);
  119. p->showText.ReplaceSel("关闭连接\r\n");
  120. }
  121. }
  122. }
  123. }
  124. }

windows socket网络编程--事件选择模型的更多相关文章

  1. windows socket 网络编程

    样例代码就在我的博客中,包含六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具project,和此小工具的全部执行时库.资源和执行程序.代码的压缩包位置是http://www.b ...

  2. windows socket网络编程资料汇集

    windows socket网络基础详解(socket的流程介绍的很详细)http://blog.csdn.net/ithzhang/article/details/8448655 Windows S ...

  3. Socket 网络编程和IO模型

    最近做了一个织机数据采集的服务器程序. 结构也非常简单,织机上的嵌入式设备,会通过Tcp 不停的往服务器发送一些即时数据.织机大改有个几十台到几百台不定把 刨去业务,先分析一下网络层的大概情况.每台织 ...

  4. windows socket网络编程基础知识

    下面介绍网络7层协议在WINDOWS的实现: 7层协议 WIN系统 ________________________________________ 7 应用层 7 应用程序 ____________ ...

  5. Windows Socket网络编程-2016.01.07

    在使用WSAEventSelect的套接字模型中,遇到了WSAEventSelect返回10038的错误,在定位解决的过程中,简单记录一些定位解决的手段摘要. 使用windows的错误帮助信息,使用命 ...

  6. windows下的socket网络编程

    windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...

  7. windows下的socket网络编程(入门级)

    windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...

  8. 转:Windows Socket五种I/O模型

    原文转自:  Windows Socket五种I/O模型 Winsock 的I/O操作: 1. 两种I/O模式 阻塞模式:执行I/O操作完成前会一直进行等待,不会将控制权交给程序.套接字 默认为阻塞模 ...

  9. Socket网络编程--Libev库学习(1)

    这一节是安装篇. Socket网络编程不知不觉已经学了快两个月了.现在是时候找个网络库学学了.搜索了很多关于如何学网络编程的博客和问答.大致都是推荐学一个网络库,至于C++网络库有那么几个,各有各的好 ...

随机推荐

  1. FFT快速傅立叶变换:解析wav波频图、Time Domain、Frequency Domain

    您好,此教程将教大家使用scipy.fft分析wav文件的波频图.Time Domain.Frequency Domain. 实际案例:声音降噪,去除高频. 结果: 波频图: Time Domain:

  2. C++基础入门:C++初始

    1. C++环境:Clion搭建 下载链接:clion官方网址 1.1 点击下载 1.2 下载对应版本 1.3 安装步骤: 1.3.1 下载完毕后,打开exe文件,进入安装界面,点击[Next > ...

  3. KingbaseES 归档日志清理

    WAL是Write Ahead Log的简写,和Oracle的redo日志类似,在R3版本存放在data/sys_log中,R6版本以后在data/sys_wal目录,在数据库访问过程中,任何对数据块 ...

  4. 一文总结高并发大数据量下MySQL开发规范【军规】

    在互联网公司中,MySQL是使用最多的数据库,那么在并发量大.数据量大的互联网业务中,如果高效的使用MySQL才能保证服务的稳定呢?根据本人多年运维管理经验的总结,梳理了一些核心的开发规范,希望能给大 ...

  5. pod(一):Kubernetes(k8s)创建pod的两种方式

    目录 一.系统环境 二.前言 三.pod 四.创建pod 4.1 环境介绍 4.2 使用命令行的方式创建pod 4.2.1 创建最简单的pod 4.2.2 创建pod,指定镜像下载策略 4.2.3 创 ...

  6. 对于Java中权限修饰符的理解

    老是把Java中权限修饰符给忘记,写一个博客加深印象吧 权限分为四个作用域:当前类,同一个包,其他包的子类,其他包的类. 首先要知道包的概念,Java中一个包是指一个package下的所有文件. pr ...

  7. 深入探究 K8S ConfigMap 和 Secret

    ConfigMap 1.什么是 ConfigMap? ConfigMap 是用来存储配置文件的 Kubernetes 资源对象,配置对象存储在 Etcd 中,配置的形式可以是完整的配置文件.key/v ...

  8. FastDFS与nginx配置使用的配置信息

    # 获取图片 location /group[1-9]/M0[0-9] { root /home/vdc1/fastdfs_storage/data; ngx_fastdfs_module; } # ...

  9. URL Search查询

    #基本查询 GET /movies/_search?q=2012&df=title&sort=year:desc&from=0&size=10&timeout= ...

  10. 2_jQuery

    一. jQuery介绍 1.1 什么是jQuery jQuery, 顾名思义, 也就是JavaScript和查询(Query), 它就是辅助JavaScript开发的js类库 1.2 jQuery核心 ...