本文由作者小林 coding 分享,来自公号“小林 coding”,有订正和改变。
1、引言
说到 TCP 协定,对于从事即时通讯 /IM 这方面利用的开发者们来说,再相熟不过了。随着对 TCP 了解的越来越深刻,很多曾今碰到过但没工夫深刻探索的 TCP 技术概念或疑难,当初是时候回头来恶补一下了。
本篇文章,咱们就从零碎层面深刻地探讨一个乏味的 TCP 技术问题:拔掉网线后,再插上,本来的这条 TCP 连贯还在吗?或者说它还“好”吗?
可能有的人会说:网线都被拔掉了,那阐明物理层(也叫实体层)被断开了(对于网络协议分层模型请见《疾速了解网络通信协定(上篇)》),那在物理层之上的传输层理当也会断开,所以本来的 TCP 连贯就不会存在的了。就如同咱们拨打有线电话的时候,如果某一方的电话线被拔了,那么本次通话就彻底断了。
答案真的是这样吗?可能并非你了解的这样哦,一起追随笔者来深入探讨一下。
学习交换:
- 挪动端 IM 开发入门文章:《新手入门一篇就够:从零开发挪动端 IM》
- 开源 IM 框架源码:https://github.com/JackJiang2…
(本文同步公布于:http://www.52im.net/thread-38…)
2、系列文章
本文是系列文章中的第 14 篇,本系列文章的纲要如下:
《鲜为人知的网络编程(一):浅析 TCP 协定中的疑难杂症(上篇)》
《鲜为人知的网络编程(二):浅析 TCP 协定中的疑难杂症(下篇)》
《鲜为人知的网络编程(三):敞开 TCP 连贯时为什么会 TIME_WAIT、CLOSE_WAIT》
《鲜为人知的网络编程(四):深入研究剖析 TCP 的异样敞开》
《鲜为人知的网络编程(五):UDP 的连接性和负载平衡》
《鲜为人知的网络编程(六):深刻地了解 UDP 协定并用好它》
《鲜为人知的网络编程(七):如何让不牢靠的 UDP 变的牢靠?》
《鲜为人知的网络编程(八):从数据传输层深度解密 HTTP》
《鲜为人知的网络编程(九):实践联系实际,全方位深刻了解 DNS》
《鲜为人知的网络编程(十):深刻操作系统,从内核了解网络包的接管过程(Linux 篇)》
《鲜为人知的网络编程(十一):从底层动手,深度剖析 TCP 连贯耗时的机密》
《鲜为人知的网络编程(十二):彻底搞懂 TCP 协定层的 KeepAlive 保活机制》
《鲜为人知的网络编程(十三):深刻操作系统,彻底搞懂 127.0.0.1 本机网络通信》
《鲜为人知的网络编程(十四):拔掉网线再插上,TCP 连贯还在吗?一文即懂!》(* 本文)
3、比拟抽象的答案
3.1 答案
引言里咱们说到:有人认为,网线都被拔掉了,那阐明物理层被断开,那么物理层之上的传输层必定也会断开,所以原来的 TCP 连贯天然也就不存在了。(PS:计算机网络分层详解请见《史上最艰深计算机网络分层详解》)
下面这个逻辑是有问题的。
问题在于:谬误的认为拔掉网线这个动作会影响传输层,事实上并不会影响!
实际上:TCP 连贯在 Linux 内核中是一个名为 struct socket 的构造体,该构造体的内容蕴含 TCP 连贯的状态等信息。
所以:当拔掉网线的时候,操作系统并不会变更该构造体的任何内容,所以 TCP 连贯的状态也不会产生扭转。
3.2 试验验证一下
我做了个小试验:我用 ssh 终端连贯了我的云服务器,而后我通过断开 wifi 的形式来模仿拔掉网线的场景,此时查看 TCP 连贯的状态没有发生变化,还是处于 ESTABLISHED 状态(如下图所示)。
通过下面试验后果能够验证我的论断:拔掉网线这个动作并不会影响 TCP 连贯的状态。
不过,这个答案还是有点抽象。实际上,咱们应该在更具体的场景中来对待这个问题,答案才更精确一些。
这个具体场景就是:
1)当拔掉网线后,有数据传输时;
2)当拔掉网线后,没有数据传输时。
针对下面这两种具体的场景,我来更具体地来剖析一下。咱们持续往下浏览。
4、具体场景 1:拔掉网线后,有数据传输时
4.1 数据传输过程中,恰好又把网线插回去了
如果是客户端被拔掉网线后,服务端向客户端发送的数据报文会得不到任何的响应,在期待肯定时长后,服务端就会触发 TCP 协定的超时重传机制(详见:《TCP/IP 详解 – 第 21 章·TCP 的超时与重传》),然而此时重传并不能失去响应的数据报文。
如果在服务端重传报文的过程中,客户端恰好把网线插回去了,因为拔掉网线并不会扭转客户端的 TCP 连贯状态,并且还是处于 ESTABLISHED 状态,所以这时客户端是能够失常接管服务端发来的数据报文的,而后客户端就会回 ACK 响应报文。
此时:客户端和服务端的 TCP 连贯将仍然存在且工作状态不会受到影响,给应用层的感觉就像什么事件都没有产生。。。
4.2 数据传输过程中,网线始终没有插回去
下面这种状况下,如果在服务端 TCP 协定重传报文的过程中,客户端始终没有将网线插回去,那么服务端超时重传报文的次数达到肯定阈值后,内核就会断定出该 TCP 有问题。而后就会通过 Socket 接口通知应用程序该 TCP 连贯出问题了,于是服务端的 TCP 连贯就会断开。
接下来,如果客户端再插回网线,如果客户端向服务端发送了数据,因为服务端曾经没有与客户端匹配的 TCP 连贯信息了,因而服务端内核就会回复 RST 报文,客户端收到后就会开释该 TCP 连贯。
此时:客户端和服务端的 TCP 连贯曾经明确被断开,本来的这个连贯也就不存在了。
4.3 刨根问底:TCP 数据报文到底重传几次?
本着知其然更应知其所以然的精力,咱们来刨根问底一下:TCP 的数据报文到底有重传几次呢?
在 Linux 零碎中,提供了一个叫 tcp_retries2 配置项,默认值是 15(如下图所示)。
如上图所示:这个内核参数是管制 TCP 连贯建设的状况下,超时重传的最大次数。
不过 tcp_retries2 设置了 15 次,并不代表 TCP 超时重传了 15 次才会告诉应用程序终止该 TCP 连贯,内核还会基于“最大超时工夫”来断定。
每一轮的超时工夫都是倍数增长的,比方第一次触发超时重传是在 2s 后,第二次则是在 4s 后,第三次则是 8s 后,以此类推。
内核会依据 tcp_retries2 设置的值,计算出一个最大超时工夫。
在重传报文且始终没有收到对方响应的状况时,先达到“最大重传次数”或者“最大超时工夫”这两个的其中一个条件后,就会进行重传,而后就会断开 TCP 连贯。
PS:无关 TCP 超时重传机制的详细情况,能够浏览《浅析 TCP 协定中的疑难杂症(下篇)》。
5、具体场景 2:拔掉网线后,有数据传输时
5.1 场景剖析
针对拔掉网线后,没有数据传输的场景,还得具体看看是否开启了 TCP KeepAlive 机制(详见《彻底搞懂 TCP 协定层的 KeepAlive 保活机制》)。
1)如果没有开启 TCP KeepAlive 机制:
在客户端拔掉网线后,并且单方都没有进行数据传输,那么客户端和服务端的 TCP 连贯将会始终放弃存在。
2)如果开启了 TCP KeepAlive 机制:
在客户端拔掉网线后,即便单方都没有进行数据传输,在继续一段时间后,TCP 就会发送 KeepAlive 探测报文。
依据 KeepAlive 探测报文响应状况,会有以下两种可能:
1)如果对端失常工作:当探测报文被对端收到并失常响应,TCP 保活工夫将被重置,期待下一个 TCP 保活工夫的到来;
2)如果对端主机解体或对端因为其余起因导致报文不可达:当探测报文发送给对端后,杳无音信、没有响应,间断几次,达到保活探测次数后,TCP 会报告该连贯曾经死亡。
所以:TCP 保活机制能够在单方没有数据交互的状况,通过 TCP KeepAlive 机制的探测报文,来确定对方的 TCP 连贯是否存活。
5.2 刨根问底:TCP KeepAlive 机制具体是什么样的?
TCP KeepAlive 机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连贯相干的流动,TCP 保活机制会开始作用,每隔一个工夫距离,发送一个探测报文。该探测报文蕴含的数据非常少,如果间断几个探测报文都没有失去响应,则认为以后的 TCP 连贯曾经死亡,零碎内核将错误信息告诉给下层应用程序。
在 Linux 内核能够有对应的参数能够设置保活工夫、保活探测的次数、保活探测的工夫距离。
以下是 Linux 中的默认值:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
解释一下:
1)tcp_keepalive_time=7200:示意保活工夫是 7200 秒(2 小时),也就 2 小时内如果没有任何连贯相干的流动,则会启动保活机制;
2)tcp_keepalive_intvl=75:示意每次检测距离 75 秒;
3)tcp_keepalive_probes=9:示意检测 9 次无响应,认为对方是不可达的,从而中断本次的连贯。
也就是说在 Linux 零碎中,起码须要通过 2 小时 11 分 15 秒才能够发现一个“死亡”连贯。
计算公式是:
留神:应用程序若想应用 TCP 保活机制须要通过 socket 接口设置 SO_KEEPALIVE 选项才可能失效,如果没有设置,那么就无奈应用 TCP 保活机制。
PS:对于 TCP 协定的 KeepAlive 机制详见《彻底搞懂 TCP 协定层的 KeepAlive 保活机制》、《一文读懂即时通讯利用中的网络心跳包机制:作用、原理、实现思路等》。
5.3 刨根问底:TCP KeepAlive 机制的探测工夫也太长了吧?
没错,的确有点长。
TCP KeepAlive 机制是 TCP 层(内核态)实现的,它是给所有基于 TCP 传输协定的程序一个兜底的计划。
实际上:咱们通常在应用层本人实现一套探测机制,能够在较短的工夫内,探测到对方是否存活。
比方:个别 Web 服务器都会提供 keepalive_timeout 参数,用来指定 HTTP 长连贯的超时工夫。如果设置了 HTTP 长连贯的超时工夫是 60 秒,Web 服务软件就会启动一个定时器,如果客户端在完后一个 HTTP 申请后,在 60 秒内都没有再发动新的申请,定时器的工夫一到,就会触发回调函数来开释该连贯。
再比方:IM、音讯推送零碎里的心跳机制,通过应用层的心跳机制(由客户端收回,服务端回复响应包),来灵便管制和探测长连贯的衰弱度。
《为何基于 TCP 协定的挪动端 IM 依然须要心跳保活机制?》这篇文章解释了 IM 这类利用中应用层心跳保活的必要性,有趣味能够读一读。
如果对应用层心跳的具体利用没什么概念,能够看看微信的这两篇文章:
《微信团队原创分享:Android 版微信后盾保活实战分享(网络保活篇)》
《挪动端 IM 实际:实现 Android 版微信的智能心跳机制》
上面有几个针对 im 这类利用的心跳实现代码,能够具体感触学习一下:
《正确理解 IM 长连贯的心跳及重连机制,并入手实现(有残缺 IM 源码)》
《一种 Android 端 IM 智能心跳算法的设计与实现探讨(含样例代码)》
《自已开发 IM 有那么难吗?手把手教你自撸一个 Andriod 版繁难 IM (有源码)》
《手把手教你用 Netty 实现网络通信程序的心跳机制、断线重连机制》
6、本文小结
上面简略总结一下文中的内容,本文结尾的问题并不是简略一句话可能精确说分明的,须要分状况看待。
也就是:客户端拔掉网线后,并不会间接影响 TCP 的连贯状态。所以拔掉网线后,TCP 连贯是否还会存在,要害要看拔掉网线之后,有没有进行数据传输。
1)有数据传输的状况:
在客户端拔掉网线后:如果服务端发送了数据报文,那么在服务端重传次数没有达到最大值之前,客户端恰好插回网线的话,那么单方本来的 TCP 连贯还是能存在并失常工作,就如同什么事件都没有产生。
在客户端拔掉网线后:如果服务端发送了数据报文,在客户端插回网线之前,服务端重传次数达到了最大值时,服务端就会断开 TCP 连贯。等到客户端插回网线后,向服务端发送了数据,因为服务端曾经断开了与客户端雷同四元组的 TCP 连贯,所以就会回 RST 报文,客户端收到后就会断开 TCP 连贯。至此,单方的 TCP 连贯都断开了。
2)没有数据传输的状况:
a. 如果单方都没有开启 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端始终不插回网线,那么客户端和服务端的 TCP 连贯状态将会始终放弃存在;
b. 如果单方都开启了 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端始终不插回网线,TCP keepalive 机制会探测到对方的 TCP 连贯没有存活,于是就会断开 TCP 连贯。而如果在 TCP 探测期间,客户端插回了网线,那么单方本来的 TCP 连贯还是能失常存在。
除了客户端拔掉网线的场景,还有客户端“宕机和杀死过程”的两种场景。
第一个场景:客户端宕机这件事跟拔掉网线是一样无奈被服务端的感知的,所以如果在没有数据传输,并且没有开启 TCP keepalive 机制时,,服务端的 TCP 连贯将会始终处于 ESTABLISHED 连贯状态,直到服务端重启过程。
所以:咱们能够得悉一个点——在没有应用 TCP 保活机制,且单方不传输数据的状况下,一方的 TCP 连接处在 ESTABLISHED 状态时,并不代表另一方的 TCP 连贯还肯定是失常的。
第二个场景:杀死客户端的过程后,客户端的内核就会向服务端发送 FIN 报文,与客户端进行四次挥手(见《跟着动画来学 TCP 三次握手和四次挥手》)。
所以:即便没有开启 TCP KeepAlive,且单方也没有数据交互的状况下,如果其中一方的过程产生了解体,这个过程操作系统是能够感知的到的,于是就会发送 FIN 报文给对方,而后与对方进行 TCP 四次挥手。
7、参考资料
[1] TCP/IP 详解 – 第 21 章·TCP 的超时与重传
[2] 通俗易懂 - 深刻了解 TCP 协定(上):实践根底
[3] 网络编程懒人入门(三):疾速了解 TCP 协定一篇就够
[4] 脑残式网络编程入门(一):跟着动画来学 TCP 三次握手和四次挥手
[5] 脑残式网络编程入门(七):面视必备,史上最艰深计算机网络分层详解
[6] 技术大牛陈硕的分享:由浅入深,网络编程学习教训干货总结
[7] 网络编程入门从未如此简略(二):如果你来设计 TCP 协定,会怎么做?
[8] 鲜为人知的网络编程(十):深刻操作系统,从内核了解网络包的接管过程(Linux 篇)
[9] 为何基于 TCP 协定的挪动端 IM 依然须要心跳保活机制?
[10] 一文读懂即时通讯利用中的网络心跳包机制:作用、原理、实现思路等
[11] Web 端即时通讯实际干货:如何让你的 WebSocket 断网重连更疾速?
(本文同步公布于:http://www.52im.net/thread-38…)