Socket编程中的强制关闭与优雅关闭及相关socket选项

2023-11-20

以下描述主要是针对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。之后再也不会等到网络事件。

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Socket编程中的强制关闭与优雅关闭及相关socket选项 的相关文章

随机推荐

  • MySQL 8.0字符集校正

    MySQL升级为8 0版本时 之前版本的字符集往往是不同的 需要校正 执行下面的三个SQL语句的查询结果 可以从库 表 列三个层面对字符集进行校正 库 select concat alter database schema name def
  • pytest的介绍与使用

    pytest介绍 pytest整合了unittest 包含了unittest中的方法 并且比其更轻便快捷 而且结合Allure报告能够清晰的生成测试报告 1 安装 pip install pytest 2 测试文件 test py test
  • C# 选择ListView 报错误:InvalidArgument=“0”的值对于“index”无效。

    加判断 if this ListView SelectedItems Count gt 0 问题解决
  • flutter 安卓打包出现:Transform‘s input file does not exist:

    我的版本 Android studio 4 0 targetSdkVersion 30 出现了这个问题 解决方案 在你的build gradle文件里面添加一句话就可以了 android studio 4 0 以上打包会出现这个问题 lin
  • 工业安全生产信息化平台的基本架构和关键功能分享

    工业安全生产信息化平台是指利用信息技术手段 将工业安全生产管理与数据采集 传输 处理相结合 实现对工业安全生产全过程的数字化 信息化 智能化管理的平台 它通过集成多种信息系统和设备 实现对重大危险源监控预警 安全风险分级管控 安全生产一张图
  • SpringBoot统一异常拦截处理

    前言 大家你好 这是我的第一篇博客 我会把我所学所悟的知识以最简单的语言描述清楚 让大家通俗易懂 正文 下面我要对springboot 1 5 8 RELEASE 中异常拦截处理进行讲解 项目中我们是一定要碰到的情况就是无论在控制层 业务层
  • 安卓平台下ARM Mali OpenCL编程-GPU信息检测

    对于ARM Mali GPU 目前是支持OpenCL1 1 所以我们可以利用OpenCL来加速我们的计算 一直以来 对于Mali GPU的OpenCL编程 一直没有环境来测试 好不容易弄到一个华为Mate7 却因为华为没有提供OpenCL
  • C#__分析使用Winform开发坦克大战

    窗口 using System using System Collections Generic using System ComponentModel using System Data using System Drawing usin
  • python梯度提升回归树_梯度提升回归树(GBDT)

    梯度提升回归树 GBDT 发布时间 2018 08 30 13 22 浏览次数 1024 标签 GBDT 一 从Boosting思想开始 1 梯度提升回归树是一种从它的错误中进行学习的技术 它本质上就是集思广益 集成一堆较差的学习算法进行学
  • 记录一下实验室的GPU信息

    通过如下查询指令 cd usr local cuda samples 1 Utilities deviceQuery sudo make deviceQuery 最终得到如下信息 Detected 1 CUDA Capable device
  • C# Socket连接请求超时处理

    在Socket的超时时间默认20多秒 而实际连上不需1秒时间 20多秒很多时候用户是不能接受的 而在等待返回结果的这段时间里程序会处于停止响应状态 废话不多说了 先上代码 private delegate string ConnectSoc
  • shell入门学习-位置变量

    1 位置变量定义 在执行脚本或命令时 传递给脚本或命令的参数 2 位置变量demo效果如下图 3 1 sh脚本如下 4 注意 如果脚本后面不输入任何参数 如下图所示 如果脚本后面只添加1个数据 如下图所示 如果脚本后面的参数超过脚本定义的位
  • 解决Review Manager(RM)很卡的方法(方法来源网络)

    1 断网 禁用网络连接 拔网线等均行 看你喜欢 2 利用Windows Win10 自带防火墙程序禁止RM联网 控制面板 网络和internet 系统和安全 windows defender防火墙 高级设置 出站规则 新建规则 下一步 此程
  • jquery的ajax获取后台数据

    前言 这里获取了小米商城的一个后台地址 效果图 源码 div p 后台拿到的总数 span style color red font size 18px span p hr ul ul div
  • 基础知识十一、Python解析网络报文之TCP首部报文解析

    文章目录 一 TCP首部解析器的实现 二 测试逻辑 上一节解析了 IP首部报文后 本节继续解析TCP报文首部 TCP协议处于OSI七层模型的传输层 传输层的作用就是负责管理端到端的通信连接问题 连续ARQ automatic repeat
  • 初始泛型类

    泛型的顶级理解 一 包装类 1 基本数据类型和对应的包装类 2 装箱和拆箱 3 自动装箱和拆箱 二 泛型 1 语法 2 泛型类的使用 3 示例 4 擦除机制 5 泛型上界 6 示例和复杂示例 7 泛型方法 一 包装类 在Java中 由于基本
  • JAVA 【爬虫】 Selenium 无头浏览,禁止加载图片,启动参数,失效,无效

    JAVA Selenium 无头浏览 禁止加载图片 启动参数 失效 无效 可能有如下几个原因 代码问题 命令参数写错 无头浏览 headless 禁止加载图片 blink settings imagesEnabled false Chrom
  • DS1302芯片介绍

    低功耗时钟芯片DS1302可以对年 月 日 时 分 秒进行计时 且具有闰年补偿等多种功能 DS1302的性能特性 实时时钟 可对秒 分 时 日 周 月以及带闰年补偿的年进行计数 用于高速数据暂存的31 8位RAM 最少引脚的串行I O 2
  • MySQL安装与使用(Windows)

    Windows平台提供了两种安装MySQL的方式 1 图形化安装 msi安装文件 链接 link 2 免安装 zip压缩文件 链接 link 安装MySQL 一 图形化安装 1 双击下载的 MySQL 安装文件 进入 MySQL 安装界面
  • Socket编程中的强制关闭与优雅关闭及相关socket选项

    以下描述主要是针对windows平台下的TCP socket而言 首先需要区分一下关闭socket和关闭TCP连接的区别 关闭TCP连接是指TCP协议层的东西 就是两个TCP端之间交换了一些协议包 FIN RST等 具体的交换过程可以看TC