求大虾指点iocp网络模型流程图

jason176374 2011-10-28 11:18:56
各位大虾,小弟最近忙于试用期考评ppt制作,内容是关于讨论高并发量的TCP服务器设计。由于最后需要有一个小小的个人演讲,小弟怕当着领导的面出丑,所以在这里罗列一些我至今似懂非懂的关于socket通讯的疑问,望获得各位大虾的点播,非常感谢~~!

1. Socket I/O数据流向图

SEND:
阻塞模式:必须等待程序缓冲区中的数据全部拷贝到TCP/IP缓冲区后才返回
非阻塞模式:查看TCP/IP缓冲区是否有剩余空间,有,则执行拷贝大小不超过剩余空间大小的程序缓冲区数据,成功后返回。无,则返回WSAEWOULDDBLOCK,提醒程序稍后再做尝试
RECV:
阻塞模式:必须等待TCP/IP缓冲区数据到达,并拷贝至程序缓冲区后才返回
非阻塞模式:查看TCP/IP缓冲区是否有数据到达,有,则执行拷贝操作成功返回。不满足,则返回WSAEWOULDDBLOCK,提醒程序稍后再做尝试

疑问1:网卡缓冲区,应该跟操作系统Socket接口无关吧?操作系统等到数据拷贝到TCP/IP缓冲区就视为I/O成功了,至于以后的事情,都交给TCP/IP协议来保证?

2. 完成端口模型中WSASend数据发送流程


疑问2:如果使用完成端口,要注意调用WSASend的次序就是就是缓冲区被填充的次序。不要从不同的线程中同时调用同一个socket上的WSASend函数,因为可能导致缓冲区中的数据处于不可预知的次序!
这句话如何理解呢? 根据我的理解WSASend是异步的I/O模式,调用WSASend时,除非TCP/IP缓冲区中有剩余空间,能够立即完成I/O操作,否则都要先将请求投递到操作系统I/O队列上,等待TCP/IP缓冲区空闲了才会执行I/O操作。
那么即使是不同线程同时调用WSASend,系统检测到之前已经有请求占用了TCP/IP缓冲区,必然会有序的将请求放入请求队列,如何又会造成不可预知的次序呢?莫非操作系统本身对于执行I/O请求操作也采用了线程池的机制吗?


3. 完成端口模型中WSARecv数据接收流程


疑问3:WSANOBUF错误产生原因:
当我们投递了很多WSARecv或者WSASend的时候,不管我们投递的Buffer有多大(0除外),系统在出现IO_PENGDING的时候,都会锁定非分页内存,系统锁定的时候,最小的锁定大小是4K(当然,这个取决于您系统的设置,也可以设置小一些,在注册表里面可以改,当然这些数值微软应该比我们更知道什么合适了)
解决办法:
1:投递WSARecv的时候,可以采用一个巧妙的设计,先投递0大小Buf的WSARecv,如果返回,表示有数据可以接收,我们开启真正的recv将数据从TCP/IP层缓冲区取出来,直到WSA_IO_PENGDING.
2:对投递的WSARecv以及WSASend进行计数统计,如果超过了我们预定义的值,就不进行WSASend或者WSARecv投递了。

在我的理解必须先投递WSARecv请求,才可以通过完成端口收到数据到达的通知,根据条款2,如果服务器本身连接了数万的Client,那么同一时刻至少要保证必须投递个数与之对应的0大小Buf的WSARecv才能确保每个Client的数据接收能正常被监测到。那如果连接数本身就超过了我们预定义的最大值,那么后来再连上来的Client,服务器不是永远都不会响应其请求了吗?我这样理解对吗?
另还有一种说法说WSASend也要先投递0字节的buf,等完成通知到达了表示TCP/IP缓冲区空间剩余可进行I/O处理,然后再调用同步的send函数进行真正的数据发送。
这样子处理的话,那我必须要对每个ClientSocket维护一个发送队列,当0字节I/O请求完成通知未到达前,对于其他线程投递的Send请求都先放入这样队列中,统一等通知到达后发送。这样子的设计是不是会有点画蛇添足的感觉的,一方面I/O又变成了同步的,另一方面程序还要自身维护一个发送队列,反而增加了系统的开销


4. 完成端口模型中AcceptEx接受连接流程


疑问4: 这几张图总感觉画的还不是很清楚,到底哪里不对也说不好,请高手指点!
...全文
379 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
teleinfor 2011-10-30
  • 打赏
  • 举报
回复
我的建议是,首先要明确你的SERVER端的线程设计模型,然后才是确定逻辑流程图。
如果不确定线程模型时序图,你如何实现逻辑框图,一股脑的把逻辑功能这么描述,并不能清晰的理清逻辑思路。你要知道这个要结合具体的实现模型才可以。

建议你绘制好线程模型,然后把每个线程的逻辑框图再绘制,就清楚了。
1、 主服务线程:负责IO完成端口以及SOCKET监听端口创建
2、 接受客户端连接线程
3、 发送数据线程
4、 接收数据线程
5、 清理现场线程(根据自己的实现确定是否需要这个线程)

供参考。
smwhotjay 2011-10-29
  • 打赏
  • 举报
回复
HarriLiu 2011-10-29
  • 打赏
  • 举报
回复
学习中
wakawaka99 2011-10-29
  • 打赏
  • 举报
回复
补充一下:
1)接收数据不用加锁解锁,因为你保证了同一时刻只有一个WSARecv(),是串行的IO请求;
2)发送数据需要加锁,因为你可以在多个业务线程中准备发送数据。

可以自己仔细想一下,思路会更清晰~
wakawaka99 2011-10-29
  • 打赏
  • 举报
回复
刚好这阵子在写proxy server,关于非分页内存问题,我采用的方法是:

1)接收数据:连接进来后投递0字节WSARecv,即wsabuf.len=0,此时非分页内存是不会增加的,如果该连接上有数据到达,则GCQS会返回,那么再投递一个实际长度的WSARecv,比如wsabuf.len=4096,此时会增加分页内存,等到QGCS再次返回时,wsabuf.buf就包含了实际数据,可以粘包处理了,最后继续投递0字节WSARecv,如此循环。这样处理,非分页内存就可以得到及时解锁,实际应用中也是这样做的。

2)发送数据:每个连接上下文独享一个发送缓存队列,那么在你想主动发数据的时候,先检查下队列有没有数据,如果有,则把要发送的数据插入队列;如果没有,直接调用WSASend()发送。等GQCS返回后,先检查下投递的数据长度是否等于真正的发送长度,如果相等,从队列中取下一条数据再次WSASend();如果小于真正发送长度,继续投递剩余缓冲区。

当然了,你得保证同一时刻,对同一套接字只有一个WSARecv()或者WSASend(),如果同一时刻同一套接字有多个WSARecv(),读到的数据会乱;如果有多个WSASend(),那么恭喜你,出现10055了~

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧