请教IOCP的DisconnectEx投递后完成事件达到顺序问题。

mynamelj 2016-12-02 08:19:36
为了在IOCP中使SOCKET句柄复用,我使用了DisconnectExt函数,一般情况下客户端与服务端建立连接后,服务端会投递一个WSARecv例程给IOCP以接收来自客户端的数据,而服务端主动断开连接时会投DisconnectEx例程。

问题描述:
当服务端主动提交DisconnectEx例程后正常情况是先收到WSARecv完成事件 然后收到DisconnectEx完成事件并且在DisconnectEx事件中做一些资源清理工作然后把此句柄提交给AcceptEx队列中。但问题是有时候先接收到的是DisconnectEx完成事件然后再接收到是WSARecv完成事件,这种情况造成一些问题比如当清理工作完成后IOCP会把这个SOCKET分配给一个新的连接,这个时候可能收到的WSARecv完成事件是原来旧的连接上的。

我在DisconnectEx之前也用过了CancelEx来取消IO但无济于世,问教大家有什么办法能在接收到断开事件前让WSARecv完成事件先达到,这样才能顺序条理的释放句柄上的资源。

...全文
1021 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
mynamelj 2020-09-23
  • 打赏
  • 举报
回复
感谢大家的回复,该问题我已经有解决方案了。 1.在投递WSARecv、WSASend事件后会增相应的引用计数,当接收到Recv/Send完成事件后对引用计递减。 2.当收到Disconnect完成事件后会先判断是否有未完成的Recv/Send例程,如果存在未完成的例程,就不作释放,并对该连接对象标记一个Closeing 3.当收到Recv/Send完成事件时判断是否有Closeing标记,如果有此标记并且没有其它未决例程时就对该对象进行释放归还给空闲列表。
worldy 2019-09-25
  • 打赏
  • 举报
回复
引用 楼主 mynamelj 的回复:
为了在IOCP中使SOCKET句柄复用,我使用了DisconnectExt函数,一般情况下客户端与服务端建立连接后,服务端会投递一个WSARecv例程给IOCP以接收来自客户端的数据,而服务端主动断开连接时会投DisconnectEx例程。

问题描述:
当服务端主动提交DisconnectEx例程后正常情况是先收到WSARecv完成事件 然后收到DisconnectEx完成事件并且在DisconnectEx事件中做一些资源清理工作然后把此句柄提交给AcceptEx队列中。但问题是有时候先接收到的是DisconnectEx完成事件然后再接收到是WSARecv完成事件,这种情况造成一些问题比如当清理工作完成后IOCP会把这个SOCKET分配给一个新的连接,这个时候可能收到的WSARecv完成事件是原来旧的连接上的。

我在DisconnectEx之前也用过了CancelEx来取消IO但无济于世,问教大家有什么办法能在接收到断开事件前让WSARecv完成事件先达到,这样才能顺序条理的释放句柄上的资源。



我是回收回来再使用
tiger波波 2019-09-24
  • 打赏
  • 举报
回复
没这么复杂吧,我记得DisconnectEx引发的WSARecv收不到任何东西或者有其他判断条件可以判断。WSARecv完成例程里要先判断这些条件的,不符合直接rerurn就行了吧。
gnorth 2019-09-21
  • 打赏
  • 举报
回复
今天正好在重新设计这一套东西,虽然这是个坟,为了以后别人少走弯路,我说下我的处理方法。 首先回答核心问题:不管你的套接字上投递了多少个事件,一旦你投递了DisconnectEx,那么所有事件都是并发返回,除非你使用了单线程IOCP,否则他们没有顺序,只有并发。 楼上的观点取决于你的服务端是不是应答式的,一问一答得机制,确实可以保证“每一个套接字上任何时间只存在一个投递事件”,这也是C1M以上并发量的做法。 但如果要求网络IO必须是异步的,就不可能满足“一个套接字上任何时间只存在一个事件” 这一条基本要求,甚至从这个问题延申出去的发送数据积压,也会同时体现出来,当然这里先不说多余的问题。那么为什么我们还需要这种方式呢?答:请参考游戏服务端。 我使用了一个IO控制类(class ioctl)来管理三个 扩展的重叠结构(OVERLAPPEDEX)对象 一个AcceptEx和WSArecv, 一个DisconnectEX, 一个WSASend. 结构大致如下: struct OVERLAPPEDEX{ OVERLAPPED ol; ioctl *ctl; char *buf; unsigned long len; int opt;//表示用来IO操作的模式: //FD_READ=WSARecv事件 //FD_WRITE=WSASend事件 //FD_ACCEPT=AcceptEx事件 //FD_CLOSE=DisconnectEx事件 }; 同时在class ioctl中,有一个int opts; 它可以是 FD_READ, FD_WRITE, FD_ACCEPT, FD_CLOSE的任意组合。 在投递操作之前,OVERLAPPEDEX.opt=具体操作;时,ioctl.opts |= 具体操作; 在事件完成时,ioctl.opts ^= OVERLAPPEDEX.opt; 读写都加锁。 这样子处理之后,那么服务端只要满足以下几个条件,就可以正确的处理这种并发问题: 1、投递AcceptEx时,必须保证ioctl.opts为0,也就是所有事件都已经返回。 2、FD_ACCEPT是一个独立的值,绝对不会包含其他操作共存,当ioctl.opts==FD_ACCEPT时,拒绝之后的所有操作。 3、FD_CLOSE可以与FD_READ,FD_WRITE共存,但是一个套接字在其有效期内,只允许投递一次FD_CLOSE。 4、一个投递过FD_CLOSE的套接字,拒绝之后的FD_READ与FD_WRITE操作。 accept:192,168,2,11:65405 2019/09/21 06:40:50 ioctl::read ok! 2019/09/21 06:40:50 IO完成:1, bytes:7 接收完毕! 收到客户端数据:啦啦啦 2019/09/21 06:40:50 ioctl::_write ok! 2019/09/21 06:40:50 IO完成:2, bytes:13 发送完毕! 2019/09/21 06:40:50 ioctl::read ok! 2019/09/21 06:40:50 ioctl::disconnect ok! 2019/09/21 06:40:50 IO完成:1, bytes:0 2019/09/21 06:40:50 IO完成:32, bytes:0 2019/09/21 06:40:50 ioctl::accept ok! 看日志可以知道,ioctl::read与ioctl::disconnect在一起调用了,之后也是并发完成,到最后一个事件完成时,才去把套接字acceptex回去。 题外话1:异步网络IO会带来发送数据积压的问题,这个问题没有什么好办法来解决,只能限制最大积压数,超过这个数量,要么断开连接,要么就把数据抛弃掉。 题外话2:异步网络IO模型并不适合IOCP来处理,应该考虑去linux使用epoll,因为异步网络IO追求的不是并发量。 题外话3:C1M以上的并发量时,IOCP将全面超越epoll,因为应答式服务端上层可以不需要任何读写锁处理,数据可以不需要任何多余的拷贝。
wowlancelot 2017-01-24
  • 打赏
  • 举报
回复
在WSASend或WSARecv成功后,使用InterlockedIncrement计数 在处理完一个IO请求后,使用InterlockedDecrement计数 当要断开连接时,标记一个状态Disconnecting=True,投递一个IO通知断开连接(PostDisconnectEx),同时计数+1 收到DisconnectEx的IO时,触发OnDisconnect后,计数-1 所有可能触发断线的地方,判断Disconnecting是否=True,若Disconnecting=True时,判断计数是否=0 计数=0时,在一个统一的接口(ReleaseClient)回收相关资源 有过程触发ReleaseClient时,表示所有IO返回完成,可以安全回收上下文资源
worldy 2017-01-24
  • 打赏
  • 举报
回复
在WSARecv完成事件接收后,在其数据处理后调用DisconnectEx,不要无序调用
mynamelj 2016-12-09
  • 打赏
  • 举报
回复
zhao4zhong1 ----------------------------------------------------- 我目前确实是这样做的,我的想法就是有没有其它更好的方法来解决这个问题!希望大家畅所欲言!
赵4老师 2016-12-09
  • 打赏
  • 举报
回复
引用 4 楼 mynamelj 的回复:
zhao4zhong1 ----------------------------------------------------- 我目前确实是这样做的,我的想法就是有没有其它更好的方法来解决这个问题!希望大家畅所欲言!
看来你我这种档次的英雄所见略同啊! 不知道还有没有更高端大气上档次的英雄供咱俩一同膜拜?
赵4老师 2016-12-08
  • 打赏
  • 举报
回复
自己弄个变量记录一下,收到不期望的WSARecv完成事件时忽略之?
mynamelj 2016-12-07
  • 打赏
  • 举报
回复
估计你没有明白我的意思,一般情况下客户端与服务端建立连接后,服务端总是会提交WSARecv例程来等待客户端发数据过来,服务端也不知道哪一个包是客户端发的最后一个包,所以正常情况下服务收到一个包之后会立即投递下一个WSARecv例程,只有客户端或服务端主动断开时才能收到最后的WSARecv的完成事件。 我所描述的问题是DisconnectEx后,WSARecv完成事件是在DiscconectEx完成事件之后到达的,这个不符合我的预期,我期望的是DisconnectEx后,WSARecv完成事件先到达DisconnectEx完成事件后达到。 希望各位能帮忙,谢谢!
Eleven 2016-12-02
  • 打赏
  • 举报
回复
你不能在WSARecv投递以后然后响应接收到数据,再这之后再投递DisconnectEx吗?

18,356

社区成员

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

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