编写大容量和健壮的服务器系列—处理IOCP连接关闭

时间:2021-11-13 02:19:42
及时监测连接被动关闭

       除非有特别要求,否则你应该总是对每个连接保持一个挂起的接收pending io

(使用WSARecv投递)。如果用户主动关闭连接,你的GetQueuedCompletionStatus调用将返回成功,但接收到的数据长度为0,你能根据这点检测连接是否已被对方关闭。如果连接被重置或者io被取消(如果你调用了CancelIo的话),GetQueuedCompletionStatus将返回失败,注意这时还应该判断GetQueuedCompletionStatus调用返回的lpOverlapped值,如果该值不为NULL,说明iocp已经检测到一个连接已经中断。

 

安全的关闭连接

       很多人写的服务器网络库有一个难以接受的缺陷(包括我曾就职公司的一些同事),当服务器程序主动关闭连接时,刚发往客户端的包有时出现丢失,这时他们推荐的方式往往是发送数据后等待几秒再关闭连接。豪无疑问,这是一种笨拙的实现方式,他们遇到的问题根源是什么呢?

       在非IOCP模式网络程序中,你只要简单的调用closesocket函数就可以确保数据在操作系统释放socket之前安全到达对方,但在IOCP模式下,如果调用closesocket时有未决的pending IO将导致socket被重置,所以有时会出现数据丢失。正统的解决方式是使用shutdown函数(指定SD_SEND标志),注意这时可能有未完成的发送pengding IO,所以你应该监测是否该连接的所有是否已完成(也许你要用一个计数器来跟踪这些pending IO),仅在所有send pending IO完成后调用shutdown。

当你调用shutdown时,也许数据仍然停留在操作系统的缓冲,操作系统将在数据发送完后发出一个FIN包来启动关闭进程,客户端接收完数据后,将接受到一个0长度的包,以此判断连接已关闭(你写的客户端肯定有检测连接关闭,不是吗?),然后调用closesocket,这时服务器的GetQueuedCompletionStatus将接收到一个数据长度为0的包,这时你就可以调用closesocket,并释放相关连接资源。

在绝大部分情况下上述的过程连接能完美的关闭。如果你特别注重服务器的安全性和健壮性,可能你还需要做一个“连接关闭队列”,对每个已调用shutdown的连接放到这个队列,然后定时的对这个队列扫描,如果一个连接5秒(你也可以自己调整)还不能关闭,那么就强制关闭它。

 

处理大并发短连接时如何避免TIME_WAIT状态
......
详细文章请参看 http://libo.deng.blog.163.com

59 个解决方案

#1


great!

#2


顶一个.

#3


收藏

#4


mark

#5


我现在遇到一些问题,能不能请教一下:
1、在完成端口中如何处理客户端连接异常断开的情形,比如客户端的网线被拔掉,IOCP服务器端如何检测到该异常并关闭该socket连接,这种情况下GetQueuedCompletionStatus返回值是0吗,是不是也能通过判断它的返回值和lpOverlapped的值来决定是否关闭socket
2、我想是否可以设置WSASend或WSARecv的时间限制,如果超时则自动关闭socket,但该如何设置,没有头绪啊
3、能不能推荐一些好的介绍完成端口编程的书籍或例子(最后能比较详细的包含各种异常的检测)

最后,非常感谢

#6


你应该加心跳包

#7


very good !

#8


up

#9


up

#10


好文,顶

#11


用户拔掉网线,基本上你是得不到异常信息的。还有,事实上,服务器在网关后面,网关会大概3分钟的时间查询一次连接,如果没有数据交互网关会主动关闭了这个连接,你可以通过这个作为检测,但是实际操作中还是要心跳来解决。
事实上完成端口最难处理的就是用户的关闭资源释放:
根据我的实际操作经验,当Get到False的时候,如果Getlasterror=64,的时候这种错误因该不用关注的,这是在操作中客户端非正常关闭连接或者程序的时候产生的。还有就是当你设置了Accept并等待返回的时候,如果用户发送了连接但并不发送数据,这时候你要检测accept的超时,这时候需要服务器主动关闭连接,Get会收到连接关闭的信号这时候产生的错误好像是995(有点忘记了)。这时候也不用关心,释放资源就可以了。因为64和995的错误避免不了,高人看看是这样的吗?
还有问个比较有难度的问题:就是说当完成端口收到任务的时候,是在完成线程体内处理好还是提交到任务处理线程池由工作线程处理。这样的切换实际的效率到底好或者坏再那里。。希望有实际操作经验的人来回答,不要只是自己的理论。。谢谢。

#12


讨论点实质的吧:我现在遇到的问题是这样的,我作转发服务器,因为不需要太复杂的处理,所以我就收到的请求就直接在完成线程里面处理了,但是遇到并发量大或者网络不好的时候,因为报文的接受不是很完整我需要保留不完整的包和下次接受到的拼包处理,这时候就有可能一次收到的处理要转发两个报文,如果循环投递2次,(因为是单cpu)就有可能出现系统一直处理这2次投递,而没时间处理其它的网络事件而导致(我这里)写完成以后完成资源来不及回收,投递的时候没有空闲的资源可用而继续增开buffer。
我现在的处理办法是每次要循环post的时候我都Sleep(5)让投递线程让出系统时间,让系统有时间去处理其它网络事件,这样运行结果就正常了。我要不要另外开处理工作线程池把接收到的请求放入线程池里面处理呢,线程之间的频繁切换也会降低效率,我现在不确定的就是Sleep(N)和在工作线程和完成线程之间切换加锁两种处理机制上,那种更好更合理?

#13


to toxyboy

做为转发,不应该理会协议,所以不需要缓冲,你收到什么就转发什么.

#14


噢,楼上,我理解了你的意思,组合成完整的报文因该是客户端来处理。。嗯,但是我不解析完整的报文,我不知道因该转发给那个客户端。还是需要缓冲收到完整的报文才可以。
还有,用工作线程来处理接收到的请求还是在完成线程里面处理更合理,这个该如何选择呢??

#15


如果你的处理有相当的复杂程度,那么就开立业务线程池进行处理,同时这个还可以减少单个连接上的数据缓存,否则会因为另一方的数据处理效率,包括接收的速率,而导致中间数据的过度缓冲,为单个连接不必要地消耗过多的内存资源.

#16


路过,学习。。

#17


学习。。。。

#18


遇到一个头大的问题,服务器中转一个500M的文件,刚开始还好,过了一会以后,服务器就只处理WSARecv事件,而一直没有WSASend返回,这时候完成健关联的资源不能被循环使用内存消耗越来越大,直到接受和转发完成,Send事件才返回。。有没有达人遇到这种问题,局域网测试是好的,发送500M的数据服务器只用了5M的资源,放到公网上,服务器内存就一直狂长,直到转发结束。。
为什么投递结束的事件直到发送结束才来得及接受处理呢??
平常中转其它短小信息的时候,写操作完成的响应都是很及时的。。。。

#19


欢迎查看IOCP系列文章新写的《编写大容量和健壮的服务器系列-处理IOCP资源释放》
blog地址: http://libo.deng.blog.163.com

#20


to toxyboy
这个需要一点技巧,我以前做过代理电信和网通这类的代理软件。
比如a->s->b
你需要判断一下,a->s已经有多少包,要有一个限,当超标时,就不需要post read.这样,当s->b转发完成了,再让s post to a read.这样,a可以继续发给s.
这样就可以了。

#21


mark

#22


up一下

#23


up

#24


mark

#25


顶一个

#26


知识帖,收藏

#27


知识帖,收藏

#28


eh,great too

#29


IOCP,不懂帮顶!

#30


我想是不是可以这样实现:
发送数据时,如果没有发送完成,则不要post read,让工作者线程始终处于发送状态;同样,
接受数据时,如果没有接收完毕(使用协议来判断),则也不post send,让它继续接收.

不好意思,小弟我刚刚接触IOCP,对其原理也不是很了解,有理解错误的话,请各位大哥(大姐)批评指正

#31


呵呵,实际上我的处理也是,对B的post send 没有完成,我不进行对A的recv 的post。当然这里要设置一个延迟,如果超时,对A  的recv 的post 还是不能阻塞的,那么对于接收方B则要进行处理了。当然这里处理还需要很多细节,特别是主动或者被动的关闭连接的时候,资源的回收问题最难处理。

#32


我测试了一周,发现投递一次和n次对于并发量大的网络服务器来说,速率影响不是很大。还有就是建议投递的数据大小4k左右最合适。

#33


mark

#34


to shenyi0106(紫色清风[if(1>0)while(1);]) 

对于一回一答式,完全可以按你所说的方式处理,但是对于服务器有可能主动推送数据的,或者某些业务的处理过程比较长,而同时客户端不需要等待该处理完成就有其它的业务可以进行并行处理的情况下,就不可以这样子处理。不过对于发送和接收分别只有一个,这是可以控制的,当然在这种情况下,可能发送就会多出一个临界区,而如果不需要考虑客户端,可能存在恶性地并过多发送连续地请求,则可以采用单一接收,多个发送的方式处理。

#35


to unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回) 

我觉得,对于服务器主动推送数据的方式,可以采用服务器端建立一张SOCKET句柄表,服务器要主动推送数据时,可以查表,然后主动调用send来发送数据.对于某些业务处理比较长,而又不能让客户端等待的情况,可以采用协议的方式,让服务器先发应答,再处理数据,业务处理完成后,在投递read,继续接受客户端数据.
当然,这都是我的理解,没有实验过,可能也不对,期待您的回复

#36


顶  标记

#37


mark

#38


mark

#39


to shenyi0106(紫色清风[if(1>0)while(1);]) 

我觉得,对于服务器主动推送数据的方式,可以采用服务器端建立一张SOCKET句柄表,服务器要主动推送数据时,可以查表,然后主动调用send来发送数据.对于某些业务处理比较长,而又不能让客户端等待的情况,可以采用协议的方式,让服务器先发应答,再处理数据,业务处理完成后,在投递read,继续接受客户端数据.
=======================
不觉得跟你之前贴子当中讲的矛盾吗?其实你的想法没有错,关键在这当中需要有一个合理地资源管理才是最重要的。IOCP本身很容易实现,处理的差别就在于资源的管理。

#40


哦,谢谢了,呵呵,其实IOCP中又些概念我不是很清楚,所以借此机会多问问,多学学,不收我学费吧,^_^

#41


mark

#42


MARK

#43


学习

#44


mqark

#45


FD_ACCEPT是客户机来connect时发生吗?我调试程序时客户机connect后,监听程序WSAWaitForMultipleEvents却没有往下执行。程序在这之前有WSAEventSelect(m_sListen,m_hAcceptEvent,FD_ACCEPT).

#46


up

#47


按照你这种写法是这样的。注册了网络ACCEPT事件,有Connect就触发。如果没执行,请查看自己的代码。

#48


不懂我也顶

#49


看看我的BLOG:
http://blog.csdn.net/wang921718/archive/2007/09/04/1772396.aspx
希望对你有所帮助

#50


up

#1


great!

#2


顶一个.

#3


收藏

#4


mark

#5


我现在遇到一些问题,能不能请教一下:
1、在完成端口中如何处理客户端连接异常断开的情形,比如客户端的网线被拔掉,IOCP服务器端如何检测到该异常并关闭该socket连接,这种情况下GetQueuedCompletionStatus返回值是0吗,是不是也能通过判断它的返回值和lpOverlapped的值来决定是否关闭socket
2、我想是否可以设置WSASend或WSARecv的时间限制,如果超时则自动关闭socket,但该如何设置,没有头绪啊
3、能不能推荐一些好的介绍完成端口编程的书籍或例子(最后能比较详细的包含各种异常的检测)

最后,非常感谢

#6


你应该加心跳包

#7


very good !

#8


up

#9


up

#10


好文,顶

#11


用户拔掉网线,基本上你是得不到异常信息的。还有,事实上,服务器在网关后面,网关会大概3分钟的时间查询一次连接,如果没有数据交互网关会主动关闭了这个连接,你可以通过这个作为检测,但是实际操作中还是要心跳来解决。
事实上完成端口最难处理的就是用户的关闭资源释放:
根据我的实际操作经验,当Get到False的时候,如果Getlasterror=64,的时候这种错误因该不用关注的,这是在操作中客户端非正常关闭连接或者程序的时候产生的。还有就是当你设置了Accept并等待返回的时候,如果用户发送了连接但并不发送数据,这时候你要检测accept的超时,这时候需要服务器主动关闭连接,Get会收到连接关闭的信号这时候产生的错误好像是995(有点忘记了)。这时候也不用关心,释放资源就可以了。因为64和995的错误避免不了,高人看看是这样的吗?
还有问个比较有难度的问题:就是说当完成端口收到任务的时候,是在完成线程体内处理好还是提交到任务处理线程池由工作线程处理。这样的切换实际的效率到底好或者坏再那里。。希望有实际操作经验的人来回答,不要只是自己的理论。。谢谢。

#12


讨论点实质的吧:我现在遇到的问题是这样的,我作转发服务器,因为不需要太复杂的处理,所以我就收到的请求就直接在完成线程里面处理了,但是遇到并发量大或者网络不好的时候,因为报文的接受不是很完整我需要保留不完整的包和下次接受到的拼包处理,这时候就有可能一次收到的处理要转发两个报文,如果循环投递2次,(因为是单cpu)就有可能出现系统一直处理这2次投递,而没时间处理其它的网络事件而导致(我这里)写完成以后完成资源来不及回收,投递的时候没有空闲的资源可用而继续增开buffer。
我现在的处理办法是每次要循环post的时候我都Sleep(5)让投递线程让出系统时间,让系统有时间去处理其它网络事件,这样运行结果就正常了。我要不要另外开处理工作线程池把接收到的请求放入线程池里面处理呢,线程之间的频繁切换也会降低效率,我现在不确定的就是Sleep(N)和在工作线程和完成线程之间切换加锁两种处理机制上,那种更好更合理?

#13


to toxyboy

做为转发,不应该理会协议,所以不需要缓冲,你收到什么就转发什么.

#14


噢,楼上,我理解了你的意思,组合成完整的报文因该是客户端来处理。。嗯,但是我不解析完整的报文,我不知道因该转发给那个客户端。还是需要缓冲收到完整的报文才可以。
还有,用工作线程来处理接收到的请求还是在完成线程里面处理更合理,这个该如何选择呢??

#15


如果你的处理有相当的复杂程度,那么就开立业务线程池进行处理,同时这个还可以减少单个连接上的数据缓存,否则会因为另一方的数据处理效率,包括接收的速率,而导致中间数据的过度缓冲,为单个连接不必要地消耗过多的内存资源.

#16


路过,学习。。

#17


学习。。。。

#18


遇到一个头大的问题,服务器中转一个500M的文件,刚开始还好,过了一会以后,服务器就只处理WSARecv事件,而一直没有WSASend返回,这时候完成健关联的资源不能被循环使用内存消耗越来越大,直到接受和转发完成,Send事件才返回。。有没有达人遇到这种问题,局域网测试是好的,发送500M的数据服务器只用了5M的资源,放到公网上,服务器内存就一直狂长,直到转发结束。。
为什么投递结束的事件直到发送结束才来得及接受处理呢??
平常中转其它短小信息的时候,写操作完成的响应都是很及时的。。。。

#19


欢迎查看IOCP系列文章新写的《编写大容量和健壮的服务器系列-处理IOCP资源释放》
blog地址: http://libo.deng.blog.163.com

#20


to toxyboy
这个需要一点技巧,我以前做过代理电信和网通这类的代理软件。
比如a->s->b
你需要判断一下,a->s已经有多少包,要有一个限,当超标时,就不需要post read.这样,当s->b转发完成了,再让s post to a read.这样,a可以继续发给s.
这样就可以了。

#21


mark

#22


up一下

#23


up

#24


mark

#25


顶一个

#26


知识帖,收藏

#27


知识帖,收藏

#28


eh,great too

#29


IOCP,不懂帮顶!

#30


我想是不是可以这样实现:
发送数据时,如果没有发送完成,则不要post read,让工作者线程始终处于发送状态;同样,
接受数据时,如果没有接收完毕(使用协议来判断),则也不post send,让它继续接收.

不好意思,小弟我刚刚接触IOCP,对其原理也不是很了解,有理解错误的话,请各位大哥(大姐)批评指正

#31


呵呵,实际上我的处理也是,对B的post send 没有完成,我不进行对A的recv 的post。当然这里要设置一个延迟,如果超时,对A  的recv 的post 还是不能阻塞的,那么对于接收方B则要进行处理了。当然这里处理还需要很多细节,特别是主动或者被动的关闭连接的时候,资源的回收问题最难处理。

#32


我测试了一周,发现投递一次和n次对于并发量大的网络服务器来说,速率影响不是很大。还有就是建议投递的数据大小4k左右最合适。

#33


mark

#34


to shenyi0106(紫色清风[if(1>0)while(1);]) 

对于一回一答式,完全可以按你所说的方式处理,但是对于服务器有可能主动推送数据的,或者某些业务的处理过程比较长,而同时客户端不需要等待该处理完成就有其它的业务可以进行并行处理的情况下,就不可以这样子处理。不过对于发送和接收分别只有一个,这是可以控制的,当然在这种情况下,可能发送就会多出一个临界区,而如果不需要考虑客户端,可能存在恶性地并过多发送连续地请求,则可以采用单一接收,多个发送的方式处理。

#35


to unsigned(僵哥(发站内消息,请附上链接或问题说明,否则不予回) 

我觉得,对于服务器主动推送数据的方式,可以采用服务器端建立一张SOCKET句柄表,服务器要主动推送数据时,可以查表,然后主动调用send来发送数据.对于某些业务处理比较长,而又不能让客户端等待的情况,可以采用协议的方式,让服务器先发应答,再处理数据,业务处理完成后,在投递read,继续接受客户端数据.
当然,这都是我的理解,没有实验过,可能也不对,期待您的回复

#36


顶  标记

#37


mark

#38


mark

#39


to shenyi0106(紫色清风[if(1>0)while(1);]) 

我觉得,对于服务器主动推送数据的方式,可以采用服务器端建立一张SOCKET句柄表,服务器要主动推送数据时,可以查表,然后主动调用send来发送数据.对于某些业务处理比较长,而又不能让客户端等待的情况,可以采用协议的方式,让服务器先发应答,再处理数据,业务处理完成后,在投递read,继续接受客户端数据.
=======================
不觉得跟你之前贴子当中讲的矛盾吗?其实你的想法没有错,关键在这当中需要有一个合理地资源管理才是最重要的。IOCP本身很容易实现,处理的差别就在于资源的管理。

#40


哦,谢谢了,呵呵,其实IOCP中又些概念我不是很清楚,所以借此机会多问问,多学学,不收我学费吧,^_^

#41


mark

#42


MARK

#43


学习

#44


mqark

#45


FD_ACCEPT是客户机来connect时发生吗?我调试程序时客户机connect后,监听程序WSAWaitForMultipleEvents却没有往下执行。程序在这之前有WSAEventSelect(m_sListen,m_hAcceptEvent,FD_ACCEPT).

#46


up

#47


按照你这种写法是这样的。注册了网络ACCEPT事件,有Connect就触发。如果没执行,请查看自己的代码。

#48


不懂我也顶

#49


看看我的BLOG:
http://blog.csdn.net/wang921718/archive/2007/09/04/1772396.aspx
希望对你有所帮助

#50


up