问题描述

今天,聊一个有趣的问题:拔掉网线几秒,再插回去,原本的 TCP 连接还存在吗?

可能有人会说,网线都被拔掉了,那说明物理层被断开了,那在上层的运输层理应也会断开,所以原本的 TCP 连接就不会存在的了。就好像, 我们拨打有线电话的时候,如果某一方的电话线被拔了,那么本次通话就会立刻中断了。

真的是这样吗?

上面这个逻辑就有问题。问题在于,错误的认为拔掉网线这个动作会影响运输层,事实上并不会影响。

实际上,TCP 连接在 Linux 内核中是一个名为 struct socket 的结构体,该结构体的内容包含 TCP 连接的状态等信息。当拔掉网线的时候,操作系统并不会变更该结构体的任何内容,所以 TCP 连接的状态也不会发生改变。

我在我的电脑上做了个小实验,我用 ssh 终端连接了我的云服务器,然后我通过断开 wifi 的方式来模拟拔掉网线的场景,此时查看 TCP 连接的状态没有发生变化,还是处于 ESTABLISHED 状态。

图1

通过上面这个实验结果,我们知道了,拔掉网线这个动作并不会影响 TCP 连接的状态。

接下来,要看拔掉网线后,双方做了什么动作。

所以, 针对这个问题,要分场景来讨论:

  • 拔掉网线后,有数据传输;
  • 拔掉网线后,没有数据传输。

场景1:拔掉网线后,有数据传输

在客户端拔掉网线后,服务端向客户端发送的数据报文会得不到任何的响应,在等待一定时长后,服务端就会触发超时重传机制,重传未得到确认的TCP报文段。

        如果在服务器端重传报文段的过程中,客户端刚好把网线插回去了,由于拔掉网线并不会改变客户端的 TCP 连接状态,并且还是处于 ESTABLISHED 状态,所以这时客户端是可以正常接收服务端发来的数据报文段的,然后客户端就会回 ACK 确认报文段。

此时,客户端和服务端的 TCP 连接依然存在的,就感觉什么事情都没有发生。

但是,如果在服务器端重传报文的过程中,客户端一直没有将网线插回去,服务器端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 连接有问题,然后通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。

而等客户端插回网线后,如果客户端向服务器端发送了数据,由于服务器端已经没有与客户端相同四元组的 TCP 连接了,因此服务器端内核就会回复 RST 报文段,客户端收到后就会释放该 TCP 连接。

此时,客户端和服务器端的 TCP 连接都已经断开了。

问题来了:那 TCP 的数据报文段具体重传几次呢?

在 Linux 系统中,内核提供了一个 tcp_retries2 系统变量,默认值是 15。

  1. # cat /proc/sys/net/ipv4/tcp_retries2

  2. 15

  3. # sysctl -a|grep tcp_retries2

  4. net.ipv4.tcp_retries2 = 15

这个内核参数是控制,在 TCP 连接建立的情况下,超时重传报文段的最大次数。

不过 tcp_retries2 设置了 15 次,并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接,内核还会基于「最大超时时间」来判定。

每一轮的超时时间都是以倍数规律增长的,比如第一次触发超时重传是在 2s 后,第二次则是在 4s 后,第三次则是 8s 后,以此类推。

图2  超时重传时间

  • RTO(Retransmission Time-Out,超时重传时间)

内核会根据 tcp_retries2 设置的值,计算出一个最大超时时间。

在重传TCP报文段且一直没有接收到对方确认的情况下,先达到「最大重传次数」或者「最大超时时间」这两个的其中一个条件后,就会停止重传,然后就会断开 TCP 连接。

参考链接】TCP协议-TCP超时重传机制

场景2:拔掉网线后,没有数据传输

针对拔掉网线后,没有数据传输的场景,还得看是否开启了 TCP keepalive 机制 (TCP 保活机制)。

如果没有开启 TCP keepalive 机制,在客户端拔掉网线后,并且双方都没有进行数据传输,那么客户端和服务端的 TCP 连接将会一直保持存在。

而如果开启了 TCP keepalive 机制,在客户端拔掉网线后,即使双方都没有进行数据传输,在持续一段时间后,TCP 就会发送探测报文段:

  • 如果对端是正常工作的。当 TCP 保活的探测报文段发送给通信对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
  • 如果对端主机崩溃,或对端由于其他原因导致报文段不可达。当 TCP 保活的探测报文段发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡

所以,TCP 保活机制可以在双方没有数据交互的情况下,通过探测报文段,来确定对方的 TCP 连接是否存活。

TCP keepalive 机制具体是怎么样的?

这个机制的原理是这样的:

设置一个时间段,在这个时间段内,如果没有发生任何数据交互的活动,TCP 保活机制会开始发挥作用,启动保活定时器,每隔一个时间间隔,发送一个探测报文段,该探测报文段包含的数据非常少(只包含一个数据字节),如果连续几个探测报文段都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序,并关闭这个 TCP 连接。

在 Linux 内核中有对应的系统变量可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9

  • tcp_keepalive_time=7200:表示保活时间间隔是 7200 秒(2小时),也就是 2 小时内如果没有任何数据交互的活动,则会启动保活机制;
  • tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
  • tcp_keepalive_probes=9:表示如果连续检测 9 次均无响应,则认为对端是不可达的,从而结束本次TCP连接。

也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」TCP 连接。

tcp_keepalive_time + (tcp_keepalive_intvl * tcp_keepalive_probes) = 7200 + (75 * 9) = 7875秒(2 小时 11 分 15秒)

注意,应用程序若想使用 TCP 保活机制需要通过 socket 接口设置 SO_KEEPALIVE 选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制。

在socket网络编程中,需要设置一个socket选项 SO_KEEPALIVE,才能开启keepalive机制。代码描述如下:

  1. keepAlive = 1;

  2. setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive));

参考链接】TCP协议-长连接和短连接

TCP keepalive 机制探测的时间也太长了吧?

对的,是有点长。

TCP keepalive 是 TCP层(内核态) 实现的,它是给所有基于 TCP 传输协议的程序一个兜底的方案。

实际上,我们也可以在应用层自己实现一套探测保活机制,可以在较短的时间内,探测到对方是否存活。

比如,Web 服务器软件一般都会提供 keepalive_timeout 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,Web 服务器软件就会启动一个保活定时器,如果客户端在完后一个 HTTP 请求后,在 60 秒内都没有再次发起新的请求,定时器的时间一到,就会触发回调函数来释放该HTTP连接。

图3  HTTP保活机制

总结

客户端拔掉网线后,并不会直接影响 TCP 连接状态。所以,拔掉网线后,TCP 连接是否还会存在,关键要看拔掉网线之后,有没有进行数据传输。

  • 有数据传输的情况:
  • 在客户端拔掉网线后,如果服务器端发送了数据报文段,那么在服务器端重传次数没有达到最大值之前,客户端就插回了网线,那么双方原本的 TCP 连接还是能正常存在,就好像什么事情都没有发生。
  • 在客户端拔掉网线后,如果服务器端发送了数据报文段,在客户端插回网线之前,服务器端重传次数达到了最大值时,服务器端就会断开本次 TCP 连接。等到客户端插回网线后,向服务器端发送数据,因为服务器端已经断开了与客户端相同四元组的 TCP 连接,所以就会回复 RST 报文段,客户端收到后就会断开本次 TCP 连接。至此, 双方的 TCP 连接都断开了。
  • 没有数据传输的情况:
  • 如果双方都没有开启 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网线,那么客户端和服务器端的 TCP 连接状态将会一直保持存在。
  • 如果双方都开启了 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网线,TCP keepalive 机制会探测到对方的 TCP 连接没有存活,于是就会断开 TCP 连接。而如果在 TCP 探测期间,客户端插回了网线,那么双方原本的 TCP 连接还是能正常存在。

除了客户端拔掉网线的场景,还有客户端「宕机和杀死进程」的两种场景。

第一个场景,客户端宕机这种情况跟拔掉网线是一样的,都无法被服务器端所感知的,所以如果在没有数据传输,并且没有开启 TCP keepalive 机制时,,服务器端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务器端重启进程。

所以,我们可以得知一个点:在没有使用 TCP 保活机制,且双方不传输数据的情况下,一方的 TCP 连接处在 ESTABLISHED 状态时,并不代表另一方的 TCP 连接也一定是正常的。

第二个场景,杀死客户端的进程后,客户端主机的操作系统内核就会向服务器端发送 FIN 报文端,与客户端进行四次挥手,然后释放 TCP 连接。

所以,即使没有开启 TCP keepalive 保活机制,且双方也没有进行数据交互的情况下,如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知得到的,于是就会发送 FIN 报文段给通信对端,然后与通信对端进行 TCP 四次挥手过程,释放本次 TCP 连接。

参考

拔掉网线后, 原本的 TCP 连接还存在吗?

TCP协议:拔掉网线后, 原本的 TCP 连接还存在吗?相关推荐

  1. 网络编程释疑之:TCP连接拔掉网线后会发生什么

    背景:前些天团队在进行终端设备和服务器端长连接业务的测试时,发现了这么一个情况:在拔掉设备端的网线后,再插上网线,有时可以继续正常的进行长接连请求,而且用的还是拔掉网线之前的那个长连接.但是有时却不能 ...

  2. 拔掉网线后, 原本的 TCP 连接还存在吗?

    大家好,我是小林. 今天,聊一个有趣的问题:拔掉网线几秒,再插回去,原本的 TCP 连接还存在吗? 可能有的同学会说,网线都被拔掉了,那说明物理层被断开了,那在上层的传输层理应也会断开,所以原本的 T ...

  3. 原本的 TCP 连接,被拔掉网线后还存在吗?

    网线都被拔掉了,那说明物理层被断开了,那在上层的传输层理应也会断开,所以原本的 TCP 连接就不会存在的了.就好像, 我们拨打有线电话的时候,如果某一方的电话线被拔了,那么本次通话就彻底断了. 真的是 ...

  4. 客户端拔掉网线后,会直接影响 TCP 连接状态吗?

    大家好,我是小林. 今天,聊一个有趣的问题:拔掉网线几秒,再插回去,原本的 TCP 连接还存在吗? 可能有的同学会说,网线都被拔掉了,那说明物理层被断开了,那在上层的传输层理应也会断开,所以原本的 T ...

  5. 【网络】拔掉网线后,TCP连接还存在吗?

    文章目录 CS 之间存在数据传输 CS 之间不存在数据传输 其他场景 这个网络问题,需要分两个场景进行讨论.[CS,指客户端与服务器] CS 之间存在数据传输 具体场景:拔掉网线之前,服务端发出数据, ...

  6. tcp协议通过什么来区分不同服务器,tcp协议通过什么来区分不同的连接

    大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答. tcp协议通过IP地址+端口号来区分不同的连接,TCP/IP是一个协议集,为应用提供一些"低级"功能,这些包括 ...

  7. TCP协议详解(一) TCP服务的特点和TCP头部结构

    从重点内容四方面来讨论TCP协议: 1:TCP头部信息.TCP头部信息出现在每个TCP报文段中,用于指定通信的源端端口号.目的端端口号.管理TCP连接,控制两个方向的数据流. 2:TCP状态转移过程. ...

  8. 00023.11 TCP协议编程:群聊(TCP通信原理,多线程、线程阻塞)

    系列文章目录 文章目录 系列文章目录 一.前言 一.需求 二.使用步骤 客户端 服务端 三.完整代码 客户端 服务器 一.前言 我们平时玩QQ或者微信的群聊,是怎么实现的呢? 是你发一个消息直接全部给 ...

  9. socket通信需要网线连接吗_从socket到TCP协议,透彻理解网络编程

    进行程序开发的同学,无论Web前端开发.Web后端开发,还是搜索引擎和大数据,几乎所有的开发领域都会涉及到网络编程.比如我们进行Web服务端开发,除了Web协议本身依赖网络外,通常还需要连接数据库,而 ...

最新文章

  1. 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | 加固厂商在 ART 下使用的两种类加载器 | InMemoryDexClassLoader 源码 )
  2. 【每日随笔】电子签名 ( 下载 “e 签保“ 应用 | 使用 手机号 + 短信验证码 登录 | 发起签署 | 签名 | 获取签名后的 PDF 文件及出证信息 )
  3. CSS 之 控制图片与文字对齐
  4. win7发现不了无线网络怎么办 win7发现不了无线网络的解决办法
  5. ubuntu 20.04双系统安装_win10上跑Ubuntu不用虚拟机不用双系统!
  6. c++用一级运算比较大小_Python 学习笔记:Python 中的数字和数字型运算
  7. linux命令:linux集群系列之一---LVS类型解析
  8. scrcpy设置快捷键_推荐电脑高清晰同步Anroid屏幕软件Scrcpy
  9. 在J.U.C多线程中,AQS维护这一个CLH同步队列,这个队列遵循着FIFO原则
  10. mysql大翻页limt 1700,100慢优化方案
  11. 【全志T113-S3_100ask】15-1 内核5.4驱动spi屏幕——ILI9341
  12. 2022年武汉安全员ABC证评分标准?多少分及格呢?甘建二
  13. MySQL中dd::columns表结构转table过程以及应用
  14. Dynamics CRM Server 2011出错,CRM Organization无法打开,无法创建和导入Organization的问题...
  15. Python:【4】利用讯飞开放平台实现语音识别
  16. 北理工计算机贾云,徐畅_北京理工大学计算机学院
  17. Xcode7最新app打包发布详细过程(一)
  18. 传递矩阵法求声波透射系数绘图求解决
  19. NSIS 运行bat的方法汇总
  20. 《墨菲定律》——职场行为学准则

热门文章

  1. 信号归一化功率_信号的频谱 频谱密度 功率谱密度 能量谱密度
  2. QTP测试脚本批处理运行的两个工具
  3. Jsplumb基础教程(vue+jsplumb+d3)
  4. [转]轻松理解MYSQL MVCC 实现机制
  5. 拷贝构造函数的类型为什么必须使用引用类型
  6. 面包板上的电子管 6J1
  7. 【蛋糕商城】(一)使用HBuliderX搭建uni-app项目
  8. Android中几种常见的播放声音组件
  9. MySQL问题记录(Linux)
  10. BP神经网络的函数逼近功能