我们在利用IOCP(完成端口)进行程序设计的时候,经常要关闭一些不满足条件的套接字。假如我们直接采用closesocket方法进行关闭的话,绑定到IO端口的此套接字的未发送的数据就会丢失,这种情况是我们不愿意发生的。下面介绍一种合理关闭此套接字的方法:
 
首先,利用setsockopt(MSDN)函数设定套接字的选项,我们把此套接字设定为:假如有数据未发送,当数据发送完后再关闭此套接字。
代码如下:
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
setsockopt(Socket, SOL_SOCKET, SO_LINGER,(char *)&lingerStruct, sizeof(lingerStruct) );//
// Now close the socket handle. This will do an abortive or graceful close, as requested.
CancelIo((HANDLE) Socket);closesocket(Socket);
clientSocket = INVALID_SOCKET;

------------------------------------------------------------------------------------------------------------------------------------------

以下描述主要是针对windows平台下的TCP socket而言。

首先需要区分一下关闭socket和关闭TCP连接的区别,关闭TCP连接是指TCP协议层的东西,就是两个TCP端之间交换了一些协议包(FIN,RST等),具体的交换过程可以看TCP协议,这里不详细描述了。而关闭socket是指关闭用户应用程序中的socket句柄,释放相关资源。但是当用户关闭socket句柄时会隐含的触发TCP连接的关闭过程。

TCP连接的关闭过程有两种,一种是优雅关闭(graceful close),一种是强制关闭(hard close或abortive close)。所谓优雅关闭是指,如果发送缓存中还有数据未发出则其发出去,并且收到所有数据的ACK之后,发送FIN包,开始关闭过程。而强制关闭是指如果缓存中还有数据,则这些数据都将被丢弃,然后发送RST包,直接重置TCP连接。

下面说一下shutdown及closesocket函数。

shutdown函数的原型是:

int shutdown(

SOCKET s,

int how

);

该函数用于关闭TCP连接,单并不关闭socket句柄。其第二个参数可以取三个值:SD_RECEIVE,SD_SEND,SD_BOTH。

SD_RECEIVE表明关闭接收通道,在该socket上不能再接收数据,如果当前接收缓存中仍有未取出数据或者以后再有数据到达,则TCP会向发送端发送RST包,将连接重置。

SD_SEND表明关闭发送通道,TCP会将发送缓存中的数据都发送完毕并收到所有数据的ACK后向对端发送FIN包,表明本端没有更多数据发送。这个是一个优雅关闭过程。

SD_BOTH则表示同时关闭接收通道和发送通道。

closesocket函数的原型是:

int closesocket(

SOCKET s

);

该函数用于关闭socket句柄,并释放相关资源。前面说过,关闭socket句柄时会隐含触发TCP连接的关闭过程,那么closesocket触发的是一个优雅关闭过程还是强制关闭过程呢?这个与一个socket选项有关:SO_LINGER 选项,该选项的设置值决定了closesocket的行为。该选项的参数值是linger结构,其定义是:

typedef struct linger {

u_short l_onoff;

u_short l_linger;

} linger;

当l_onoff值设置为0时,closesocket会立即返回,并关闭用户socket句柄。如果此时缓冲区中有未发送数据,则系统会在后台将这些数据发送完毕后关闭TCP连接,是一个优雅关闭过程,但是这里有一个副作用就是socket的底层资源会被保留直到TCP连接关闭,这个时间用户应用程序是无法控制的。

当l_onoff值设置为非0值,而l_linger也设置为0,那么closesocket也会立即返回并关闭用户socket句柄,但是如果此时缓冲区中有未发送数据,TCP会发送RST包重置连接,所有未发数据都将丢失,这是一个强制关闭过程。

当l_onoff值设置为非0值,而l_linger也设置为非0值时,同时如果socket是阻塞式的,此时如果缓冲区中有未发送数据,如果TCP在l_linger表明的时间内将所有数据发出,则发完后关闭TCP连接,这时是优雅关闭过程;如果如果TCP在l_linger表明的时间内没有将所有数据发出,则会丢弃所有未发数据然后TCP发送RST包重置连接,此时就是一个强制关闭过程了。

另外还有一个socket选项SO_DONTLINGER,它的参数值是一个bool类型的,如果设置为true,则等价于SO_LINGER中将l_onoff设置为0。

注意SO_LINGER和SO_DONTLINGER选项只影响closesocket的行为,而与shutdown函数无关,shutdown总是会立即返回的。

所以为了保证建议的最好的关闭方式是这样的:

发送完了所有数据后:

(1)调用shutdown(s, SD_SEND),如果本端同时也接收数据时则执行第二步,否则跳到第4步。

(2)继续接收数据,

(3)收到FD_CLOSE事件后,调用recv函数直到recv返回0或-1(保证收到所有数据),

(4)调用closesocket,关闭socket句柄。

在实际编程中,我们经常也不调用shutdown,而是直接调用closesocket,利用closesocket隐含触发TCP连接关闭过程的特性。此时的过程就是:

当发送完所有数据后:

(1)如果本端同时也接受数据则则执行第二步,否则跳到第4步。

(2)继续接收数据,

(3)收到FD_CLOSE事件后,调用recv函数直到recv返回0或-1(保证收到所有数据),

(4)调用closesocket,关闭socket句柄。

但是此时为了保证数据不丢失,则需要设置SO_DONTLINGER选项,不过windows平台下这个也是默认设置。

经过实验发现,发送端应用程序即便是异常退出或被kill掉进程,操作系统也不会丢弃发送缓冲区中的未发送数据,而是会在后台将这些数据发送出去。但是这是在socket的发送缓存不为0的前提下,当socket的发送缓存设置为0(通过SO_SNDBUF选项)时比较特殊,此时不论socket是否是阻塞的,send函数都会被阻塞直到传入的用户缓存中的数据都被发送出去并被确认,因为此时在驱动层没有分配缓存存放用户数据,而是直接使用的应用层的用户缓存,所以必须阻塞直到数据都发出,否则可能会造成系统崩溃。

另外,如果是接收端的应用程序异常退出或被kill掉进程,并且接收缓存中还有数据没有取出的话,那么接收端的TCP会向发送端发送RST包,重置连接,因为后续数据已经无法被提交应用层了。

最后这里说一个感觉是windows的bug,就是做这样的一个测试:

在一端线listen一个socket,然后在另一端connect,connect成功后,listen端会检测到网络事件触发,在listen端accept之前,将connect端kill掉,然后继续运行listen端,listen端任然会accept成功,且在accept出来的socket发送数据也能成功。发送完之后在等网络事件,此时又会等待成功,但是调用WSAEnumNetworkEvents得出的事件标识却是0。之后再也不会等到网络事件。

异步socket优雅的关闭-CancelIO和SO_LINGER相关推荐

  1. c#异步socket

    .NET Socket开发之异步Socket 在基于.NET的网络服务端的开发中,我们用到和听到的最多的恐怕就是异步Socket了.异步Socket的性能比同步高出很多,但是编写代码比较复杂.因此异步 ...

  2. C#.net同步异步SOCKET通讯和多线程总结(转)

    C#.net同步异步SOCKET通讯和多线程总结 来源:http://www.cnblogs.com/Silverlight_Team/archive/2009/03/13/1411136.html ...

  3. python模块介绍-asyncore 异步socket处理器

    2019独角兽企业重金招聘Python工程师标准>>> 该模块提供了异步socket服务客户端和服务器的基础架构. 只有两种方法让程序在单个处理器上的同时做超过一件的事情. 多线程编 ...

  4. java 关闭阻塞线程池_如果优雅地关闭ExecutorService提供的java线程池

    每一个线程都会占用系统资源,因此线程池的关闭与清理同样重要,本文介绍我们如何优雅地关闭线程池. 一. ExecutorService中关闭线程池的方法 1. shutdown() 停止接收新任务,原来 ...

  5. 可扩展多线程异步Socket服务器框架EMTASS 2.0

    本文原创版权归 CSDN hulihui 所有,转载请按照如下方式显示标明原创作者及出处,以示尊重!! 作者: hulihui 原文:http://blog.csdn.net/hulihui/arch ...

  6. 游戏客户端与服务器通讯协议,Jtro的技术分享:游戏客户端与服务器(c#)通讯_异步Socket...

    首先跟大家道个歉,上一个同步Socket文章里用的不是Markdown编写的,所以代码看起来不是很清爽,我用的鹅厂的浏览器,终于发现是浏览器的锅,图片拖不上去 -_-|| , 真的是很失败啊,现在好了 ...

  7. 如何优雅的关闭容器,看这一篇就够了

    最近把 Docker 官方的 Docker Reference 文档又读了一遍,发现有些细节深究起来,还是有很多可挖的.针对写 Dockerfile ,大部分时候只要照葫芦画瓢,基本也不会有什么大的问 ...

  8. Effective java 系列之更优雅的关闭资源-try-with-resources

    背景: 在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在 ...

  9. 高并发 python socket send 异步_在Python中使用异步Socket编程性能测试

    ok,首先写一个python socket的server段,对开放三个端口:10000,10001,10002.krondo的例子中是每个server绑定一个端口,测试的时候需要分别开3个shell, ...

最新文章

  1. Java 的zip压缩和解压缩
  2. 计算机、数学、运筹学等领域32个重要算法
  3. 分布式服务框架Dubbo使用小结
  4. eclipse下的tomcat内存设置大小(转)
  5. Julia :PyPlot的plot_date
  6. JUCE框架教程(2)—— 创建一个基本的音频/MIDI 插件第一部分:设置
  7. echarts:中国地图-省市区二级联动
  8. html页面加载蒙版,HTML / CSS - 在图像上创建alpha蒙版
  9. python刷火车票脚本_Python打造刷火车票工具邮件通知自己
  10. Blender创作流程01-软硬件配置
  11. php接入外汇购物,兑换难!这些外币最好别带回国
  12. (转)eclipse 打开pom.xml文件很慢 设置pom.xml打开方式
  13. Ceph之RBD恢复的几种方式与原理
  14. 2022年上海市安全员C证考试题库及答案
  15. LinearLayout布局添加下划线
  16. 初学3D制作,先学C4D还是Blender?
  17. 山西省大学计算机专业排名,山西省:排名前14的大学!山西的大学分为5档,前2档最难考!...
  18. python图像处理教程,【图像处理】使用OpenCV+Python进行图像处理入门教程(二)...
  19. #174-D: expression has no effect报错解决方法
  20. python3 爬虫之爬取糗事百科

热门文章

  1. Spring框架的前世今生以及对Spring的宏观认识
  2. 使用Commons Logging - Java异常处理
  3. 云服务器 web网站吗,web云服务器免费的吗
  4. opencv轮廓周长原理_OpenCV3 - 轮廓特征
  5. 服务器升级中不能修改信息,服务器升级页面
  6. qt 运行库 linux,linux(ubuntu) 版qt5.x安装的一些知识
  7. 如何反复读取同一个 InputStream 对象
  8. Linux文件目录命名规则
  9. 步进电机控制芯片_STK682/步进电机_STK682-010-E控制芯片 原创中文翻译
  10. 粘性控件,滑动停留StickLayout(导航栏滑动停留)