Winsock异步模式I/O模型WSAEventSelect的使用
1.Winsock同步阻塞方式的问题 在异步非阻塞模式下,像accept(WSAAccept),recv(recv,WSARecv,WSARecvFrom)等这样的winsock函数调用后马上返回,而不是等待可用的连接和数据。在阻塞模式下,server往往这样等待client的连接: while(TRUE) 上述代码简单易用,但是缺点在于如果没有client连接的话,accept一直不会返回,而且即使accept成功创建会话套接字,在阻塞方式下,C/S间传输数据依然要将recv,send这类函数放到一个循环中,反复等待数据的到来,这种轮询的方式效率很低。为此,Winsock提供了异步模式的5种I/O模型,这些模型会在有网络事件(如socket收到连接请求,读取收到的数据请求等等)时通过监视集合(select),事件对象(WSAEventSelect,重叠I/O),窗口消息(WSAAsyncSelect),回调函数(重叠I/O),完成端口的方式通知程序,告诉我们可以“干活了”,这样的话大大的提高了执行效率,程序只需枕戈待旦,兵来将挡水来土掩,通知我们来什么网络事件,就做相应的处理即可。 2.WSAEventSelect模型的使用 WSAEventSelect模型其实很简单,就是将一个事件对象同一个socket绑定并设置要监视的网络事件,当这个socket有我们感兴趣的网络事件到达时,ws2_32.dll就将这个事件对象置为受信状态(signaled),在程序中等待这个事件对象受信后,根据网络事件类型做不同的处理。如果对线程同步机制有些了解的话,这个模型很容易理解,其实就是CreateEvent系列的winsock版。 无代码无真相,具体API的参数含义可以参考MSDN,MSDN上对这个模型解释的非常详尽。
// 使用WSAEventSelect的代码片段,百度贴吧字数限制,略去错误处理及界面操作
// 为了能和多个客户端通信,使用两个数组分别记录所有通信的会话套接字 // 以及和这些套接字绑定的事件对象 // WSA_MAXIMUM_WAIT_EVENTS是系统内部定义的宏,值为64 SOCKET g_sockArray[WSA_MAXIMUM_WAIT_EVENTS]; // 事件对象计数器 // 创建监听套接字sListenSocket,并对其绑定端口和本机ip 代码省去 // 设置sListenSocket为监听状态 // 创建事件对象,同CreateEvent一样,event创建后被置为非受信状态 // 将sListenSocket和acceptEvent关联起来 // 添加到数组中 // 处理网络事件 // 取得受信事件在数组中的位置。 // 判断受信事件 g_eventArray[nIndex] 所关联的套接字 g_sockArray[nIndex] 的网络事件类型 // 这里处理 FD_ACCEPT 这个网络事件 // 新建的会话套接字用于C/S间的数据传输,所以这里关心FD_READ,FD_CLOSE,FD_WRITE三个事件 // 将新建的会话套接字及与该套接字关联的事件对象添加到数组中 //event.iErrorCode[FD_ACCEPT_BIT] != 0 出错了 // 这里处理FD_READ通知消息,当会话套接字上有数据到来时,ws2_32.dll会记录该事件 // event.iErrorCode[FD_READ_BIT] != 0 // 这里处理FD_CLOSE通知消息 // event.iErrorCode[FD_CLOSE_BIT] != 0 // 处理FD_WRITE通知消息 // 如果出错退出循环 则将套接字数组中的套接字与事件对象统统解除关联 for(int i = 0; i < nEventTotal; i++) nEventTotal = 0; DoSomethingElse.... 3.FD_WRITE 事件的触发 常见的网络事件中,FD_ACCEPT和FD_READ都比较好理解。一开始我唯一困惑的就是FD_WRITE,搞不清楚到底什么时候才会触发这个网络事件,后来仔细查了MSDN又看了一些文章并测试了下,终于搞懂了FD_WRITE的触发机制。 下面是MSDN中对FD_WRITE触发机制的解释: The FD_WRITE network event is handled slightly differently. An FD_WRITE network event is recorded when a socket is first connected with connect/WSAConnect or accepted with accept/WSAAccept, and then after a send fails with WSAEWOULDBLOCK and buffer space becomes available. Therefore, an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK. After such a failure the application will find out that sends are again possible when an FD_WRITE network event is recorded and the associated event object is set FD_WRITE事件只有在以下三种情况下才会触发 ①client 通过connect(WSAConnect)首次和server建立连接时,在client端会触发FD_WRITE事件 ②server通过accept(WSAAccept)接受client连接请求时,在server端会触发FD_WRITE事件 ③send(WSASend)/sendto(WSASendTo)发送失败返回WSAEWOULDBLOCK,并且当缓冲区有可用空间时,则会触发FD_WRITE事件 ①②其实是同一种情况,在第一次建立连接时,C/S端都会触发一个FD_WRITE事件。 主要是③这种情况:send出去的数据其实都先存在winsock的发送缓冲区中,然后才发送出去,如果缓冲区满了,那么再调用send(WSASend,sendto,WSASendTo)的话,就会返回一个 WSAEWOULDBLOCK的错误码,接下来随着发送缓冲区中的数据被发送出去,缓冲区中出现可用空间时,一个 FD_WRITE 事件才会被触发,这里比较容易混淆的是 FD_WRITE 触发的前提是 缓冲区要先被充满然后随着数据的发送又出现可用空间,而不是缓冲区中有可用空间,也就是说像如下的调用方式可能出现问题 else if(event.lNetworkEvents & FD_WRITE) 问题在于建立连接后 FD_WRITE 第一次被触发, 如果send发送的数据不足以充满缓冲区,虽然缓冲区中仍有空闲空间,但是 FD_WRITE 不会再被触发,程序永远也等不到可以发送的网络事件。 基于以上原因,在收到FD_WRITE事件时,程序就用循环或线程不停的send数据,直至send返回WSAEWOULDBLOCK,表明缓冲区已满,再退出循环或线程。当缓冲区中又有新的空闲空间时,FD_WRITE 事件又被触发,程序被通知后又可发送数据了。 上面代码片段中省略的对 FD_WRITE 事件处理
else if(event.lNetworkEvents & FD_WRITE)
{ if(event.iErrorCode[FD_WRITE_BIT] == 0) { while(TRUE) { // 得到要发送的buffer,可以是用户的输入,从文件中读取等 GetBuffer.... if(send(g_sockArray[nIndex], buffer, buffersize, 0) == SOCKET_ERROR) { // 发送缓冲区已满 if(WSAGetLastError() == WSAEWOULDBLOCK) break; else ErrorHandle... } } } else { ErrorHandle.. break; } } P.S. 1.WSAWaitForMultipleEvents内部调用的还是WaitForMulipleObjectsEx,MSDN中说使用WSAEventSelect模型等待时是不占cpu时间的,这也是效率比阻塞winsock高的原因。 2.WSAAsycSelect的用法和WSAEventSelect类似,不同的是网络事件的通知是以windows消息的方式发送到指定的窗口。 |
Winsock异步模式I/O模型WSAEventSelect的使用相关推荐
- Winsock的异步模式的I/O模型
Winsock的异步模式的I/O模型 闲的没事看了下Winsock的异步模式的I/O模型,写些体会和感悟,记录一下. 1.Winsock同步阻塞方式的问题1 C0 l/ W8 {2 k 在异步非阻塞模 ...
- WinSock异步编程
WinSock异步编程 文章目录 WinSock异步编程 简介 WSAAsyncSelect Finger协议 Finger服务器程序 Finger客户端程序 简介 同步 每个函数在下一条语句执行以前 ...
- Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结
同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 由处理消息者自己去等待消息是否被触发 我去银行办理业务,选择排队等,排到头了就办理. 异步模式 由触发机制来通知处理消息者 我去银行办 ...
- APM之基于事件的异步模式(EAP)-2
EAP是针对Windows窗体开发提供的方便使用的异步模式,可以在IDE中可视化的设计和使用 // The System.Net.WebClient class supports the Event- ...
- Linux网络编程 | 并发模式:半同步/半异步模式、领导者/追随者模式
文章目录 同步与异步 半同步/半异步模式 变体:半同步/半反应堆模式 改进:更高效的半同步/半异步模式 领导者/追随者模式 组件 :句柄集.线程集.事件处理器 并发模式是指I/O处理单元和多个逻辑单元 ...
- 与其他.Net异步模式和类型进行互操作
返回该系列目录<基于Task的异步模式--全面介绍> Tasks和异步编程模型APM(Tasks and the Asynchronous Programming Model) 从APM到 ...
- 高效的半同步/半异步模式的实现
先介绍一下半同步/半异步模式: 首先半同步/半异步模式中的同步和异步和前面的IO模型中的同步和异步是完全不用的概念.在IO模型中,同步和异步区分的是内核向应用程序通知的是何种IO事件(是就绪事件还是完 ...
- 基于事件的异步模式概述
基于事件的异步模式概述 MSDN 那些同时执行多项任务.但仍能响应用户交互的应用程序通常需要实施一种使用多线程的设计方案.System.Threading 命名空间提供了创建高性能多线程应用程序所必需 ...
- 半同步半异步模式 -------一个架构模式,清晰的结构,高效并发的I/O
译者: cuichaox@gmail.com 英文原文: http://www.cs.wustl.edu/~schmidt/PDF/HS-HA.pdf http://www.cs.wustl.edu/ ...
最新文章
- TensorFlow学习笔记——实现经典LeNet5模型
- matlab paticalcoff,关于DOA估计中加权前后向空间平滑算法的仿真问题
- plsql如何连接oracle11g_PLSQL连接Oracle11G图文教程(含PLSQL配置文件)
- ElementUI中的el-table中实现动态添加一行、删除一行、清空所有行
- linux查看磁盘占用
- LeetCode MySQL 597. 好友申请 I :总体通过率
- mysql 8 my.cfg_搭建lamp环境以及安装配置phpmyadmin
- Maven本地库添加Oracle JDBC驱动
- oracle--逻辑对象--bai
- [深入理解C++(一)]类型转换(Type Casting)
- 关于SOA的应用研究思考
- zigbee芯片cc2430资料
- 矩阵标准型的系数是特征值吗_数据分析基础:特征值和特征向量
- 使用Stellarium Web跟踪浏览器中的星星
- win10系统连接不上服务器,win10系统电驴连接不上服务器的解决方法
- 示波器的带宽和采样率
- MTK6737 WCN省晶体问题
- mysql五日均线_mysql – 如何在SQL中的某个日期范围内执行移动平均线?
- 设计模式之美总结(创建型篇)
- 项目推荐:人工智能公有链——Project PAI