S.l.e!ep.¢%

像打了激速一样,以四倍的速度运转,开心的工作
简单、开放、平等的公司文化;尊重个性、自由与个人价值;
posts - 1098, comments - 335, trackbacks - 0, articles - 1
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

编写大容量和健壮的服务器系列—处理IOCP连接关闭  2007-08-11 14:45

分类:默认分类
字号:    

 

 

邓立波 深圳,2007-8

作者联系方式 :

email:          libodeng@gmail.com

msn:           libodeng@gmail.com

tel:              13510275799

版权/著作权所有 (C) 2007 邓立波 保留所有权利

警告:未经作者许可,任何人或组织不得转载,公开发布,拷贝,传播本文献的全部或部分

  

及时监测连接被动关闭

       除非有特别要求,否则你应该总是对每个连接保持一个挂起的接收 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 状态

       关于如何避免 TIME_WAIT 这个问题,一直没看到有效的处理方式(至少我没有), 我将在这里披露一种有效的方式。回到上一段,我们最后调用了 closesocket 关闭连接,这时仍然可能出现 TIME_WAIT 状态,但注意这时所有的数据都已经传输完毕,因此你可以强制关闭 socket 避免服务器连接进入 TIME_WAIT (这时只会发出连接重置 RESET 包)

// 立即关闭 ( 避免出现 TIME_WAIT 状态 )

              LINGER linger = {1,0};

              setsockopt(socket, SOL_SOCKET, SO_LINGER,

                     (char *)&linger, sizeof(linger));

 

 

socket唯一性问题

正常情况下 SOCKET 套结字值是唯一的,但是操作系统在分配 socket 值时有随机性,最近关闭的 socket 值可能重新分派给一个刚刚建立的新的 socket. ,尤其在大并发短连接的情况下。一个健壮的服务器 IOCP 网络库必须要考虑 socket 唯一性的问题,由于 IOCP 的排队机制,意味着当你调用 closesocket 关闭 socket 后, IOCP 队列中可能仍然堆积了该 socket 的一些 I/O completion packet ,而此时,刚关闭的 socket 值又分派给一个刚刚建立的 socket ,所以,你必须对 GetQueuedCompletionStatus 获取到的 I/O completion packet 小心翼翼处理,避免出现数据混乱,然而,最好的方式等到所有 I/O completion packet 返回后才调用 closesocket 关闭该 socket

 

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理