IOCP你不知道的那些事

问题提出:

在Accept或者Acceptex客户端连接以后,按照客户端设计方案,投递第一次WSARecv,在GetQueuedCompletionStatus中,将获取投递该WSARecv成功获取数据的事件通知,为了考虑数据传输的方便性,我们将获取到的数据存储到一个队列中,供后面另外独立的线程处理。


在该独立线程中,由事件唤醒,我们分析完毕数据以后,对于每个独立的数据包或者说每个客户端投递一次WSASend,正是由于这个投递的WSASend造成了服务器的GetQueuedCompletionStatus多次返回,并且数据除了第一次正确获取之外,其他都为空数据,我想应该是我投递WSASend造成了IOCP队列出现了问题。

但是始终想不明白问题在何处,以及为什么出现这个问题。

完整代码下载:
http://www.server-development.cn/post/iocp-server-client.php

希望哪位仁兄可以帮我分析一下。

就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱。  

当然我想应该是我处理的不当?


回复于:2008-04-27 18:02:13
你是说对所有的客户端发出WSASend,那么当然每个发送完成都会产生一次IOCP信号,导致GetQueuedCompletionStatus返回。

回复于:2008-04-27 18:22:05
就是在获取到数据包之后,另外的处理线程投递WSASend,使用何种方法不会造成GetQueuedCompletionStatus多次返回,也就是说我猜测是投递的WSASend造成了IOCP队列的混乱
----------------
IO队列并不混乱,只要你调用WSASend和WSARecv之类的函数 就相当于向IOCP投递一个请求,每发一次请求都会导致
GetQueuedCompletionStat函数返回,而下面没见你怎么区分两者,也许是我没看仔细....还有想问下在2005下对stack和list操作是线程安全的么?如果不是安全的就得加个锁

回复于:2008-04-27 18:31:08
没看代码,对完成端口的任何投递操作都会引起GetQueuedCompletionStatus返回,需要在GetQueuedCompletionStatus后进行区分,可以在WSAOVERLAPPED的尾部放一些数据来区分这些操作
typdef struct {
WSAOVERLAPPED d;
int opType;
}Op;
Send时候这样WsaSend(..., (WSAOVERLAPPED)new Op {..., sendOp}, NULL); //示意代码,结构体需要强转
GetQueuedCompletionStatus回来后,把WSAOVERLAPPED指针强转为Op类型,就可以取到是sendOp的类型了。
回复于:2008-04-27 20:07:25
Applications use WSACreateEvent() to obtain an event object handle which may then be supplied as a
required parameter to the overlapped versions of send and receive calls (WSASend(), WSASendTo(),
WSARecv(), WSARecvFrom()). The event object, which is cleared when first created, is set by the
transport providers when the associated overlapped I/O operation has completed (either successfully or with
errors). Each event object created by WSACreateEvent() should have a matching WSACloseEvent() to
destroy it.

看来IOCP也是一种事件机制 一个Queue队列管理的事件通知队列

当投递一个WSARecv的时候,只是将其加入Queue队列
当系统处理完该队列的时候 设置该事件的触发

然后GetQueuedCompletionStatus去查询一个事件组 如果发现一个事件完成 就返回
而多个线程通过GetQueuedCompletionStatus去查询这个组 这个事件组应该也是一个锁操作
如果某一个线程处理的时候 其他应该是不可以的
又由于Queue的先进先出原则 可以保证包的顺序

一点点个人理解 大家指点一下
回复于:2008-04-27 20:21:14
你既然投递了收发事件就必须有针对的处理,否则不乱么?

WSASend投递 而这个投递也是导致GetQueuedCompletionStatus多次返回的原因,数据大部分是空
---------------------
WSASend投递 的话 会触发GetQueuedCompletionStatus返回,在返回时可以判断是否发送成功,它又不是接收,数据当然基本是空的,同时他也告诉你:你又可以投递下一个发送请求了
回复于:2008-04-27 20:51:47
对于TCP,个人建议对任一个连接,永远只有一个未决的WSARecv,以保证顺序,并且对于判断数据的完整性分割业务报文之前不要发起下一个WSARecv.
对于发送,有两种处理,一种处理就是在发送的时候,采用发送队列,这种方式对于一个连接上面的处理是存在一定的效率问题,但是对于整个服务器的服务方案来讲,并不存在效率问题,并且还可以大大缓和竞争。确实可以为争取单个连接的效率,以及处理上面省去一部分复杂性,可以对一个连接发送多个WSASend,但是在这当中就存在 “何时结束连接,并释放相关资源”的问题,没有引用计数,这就是一个非常严重的问题。
回复于:2008-04-27 21:38:55
引用 17 楼 madmanahong 的回复:
不过对于WSASend来说,当然不要每次发送一个,否则丧失了overlap的性能。 

使用引用计数或者职能指针可以解决这个问题。 


前面我少掉到了一个点,那就是WSASend的未决数量在整个系统当中是有限的。所以作为服务器,不可能也没有必要去争取针对单连接体现的效率。
回复于:2008-04-27 21:44:57
另外就是在这种应用当中,智能指针并不能替代引用计数,智能指针的“智能”是在编译器当中实现的,而并不是被系统所支持的,也就是说,当你把比如一个Overlapped的指针,交给系统之后,就不能实现这种所谓的“智能”,或者说这个所谓的“智能”并没有实在的意义。智能指针只是为了编程当中减少野指针产生的一个有限的解决方法,这个并不能管理一个具备了除socket描述符以外还包含有扩充性数据资源的一个socket上下文描述结构,这个结构当你有多个未决的I/O请求时,将需要被多次使用,而不是一次。
回复于:2008-04-27 21:50:15
另外一种应用就是不需要任何扩充性的结构跟socket进行关键,任何一个处理都只跟socket相关,这种应用相对来说,也就比较有限,在很多时候作为服务器,这种相当于socket的附加描述信息的资源结构,还是有非常重要意义的。比如说心跳超时断开,等等。
回复于:2008-04-27 22:23:13
引用 21 楼 huzhangyou 的回复:
to 僵哥:很感谢你的回复。 

对于我的问题现在明朗起来了,如果我需要在另外的线程中投递WSASend是否需要新建整个Context对象,并在这个Context对象上面进行处理 而不是简单的将Get返回的Context上面去投递WSASend 

希望我表达清楚了我的意思 

to madmanahong :的确 unsigned明朗了几个疑惑 感谢您的参议 

Context对象复制是没有意义的,一个socket在accept之后并用CreateIoCompletionPort绑定之后,这个Context就已经被确定,即便你重新申请一个,那么在GetQueuedCompletionStatus当中也无法得到这个新的Context,仍然返回的还是原来的。除非你不去理会它,而是把这个Context关联到Overlapped结构,需要明确的是Context(在GetQueuedCompletionStatus当中由第三个参数返回)之所以在相关的资料当中都命名为PerHandle*,就是因为,它针对socket(Handle)是唯一的,从CreateIOCompletionPort关联之后,一直伴随着这个socket的,另一个Overlapped结构则命名为PerIO*(在GetQueuedCompletionStatus当中由第四个参数返回),即,它在每一次I/O操作当中都是唯一的。而我前面提到的就是指这个PerHandle*(暂定为Context)何时释放的问题,如果你有一个WSARecv关联其中,那么你释放了,其它的WSASend将无法再使用该结构指针(此时已经是野指针),但是由于在多线程系统当中,这只是一个瞬息的时间差,根本就无法判断(如果使用临界区确实可以达到同步的目的,但是对于“高性能”的应用,显得得不尝失), 之所以很多人不能把完成端口写好,问题也就出在这里,基本上这可以被视为IOCP的一个关键。前面仅仅只是说一个WSARecv+一个WSASend就存在这样的问题,那多加几个WSASend,问题依旧,而大家可能也都注意到了,包括Microsoft Platform SDK当中的实例在内的很多所谓的Demo,都是做的一个简单的Echo Server,在他们的处理当中都只是一个WSARecv或者一个WSASend在工作,而并没有给出解决这一问题的解决方案。但是,也并不能说这些Demo是不合理或者无效的,对于服务器,比如SMTP等等,他们本来就只是简单的“一问一答”式的服务,自然就可以这样子简简单单地处理。


做个记录:

在IOCP 框架中 GetQueuedCompletionStatus 在一个工作线程WorkThread中被调用,目的是从队列Queue中不断获取数据,(先进先出的方式)

调用 WSARecv ,WSASend 就是为了投递消息,这个时候 GetQueuedCompletionStatus 所在的线程就被激活,继续执行下面的操作,通过 GetQueuedCompletionStatus 的参数来进行各种操作判断,

调用 PostQueuedCompletionStatus 的目的好像大多数是为了发送消息给工作者线程让等待的线程退出。


回复比较多,都是围绕IOCP来进行探讨的,有些回复比较给力

地址:

http://topic.csdn.net/u/20080427/16/9aadc7dc-141c-4ac7-83b3-446dec5c8620.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值