Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 17 题:A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态 #21

Open
ray1888 opened this issue Feb 24, 2019 · 20 comments
Labels

Comments

@ray1888
Copy link

ray1888 commented Feb 24, 2019

因为B会在重启之后进入tcp状态机的listen状态,只要当a重新发送一个数据包(无论是syn包或者是应用数据),b端应该会主动发送一个带rst位的重置包来进行连接重置,所以a应该在syn_sent状态

@jjeejj
Copy link
Contributor

jjeejj commented Feb 24, 2019

@ray1888 你给这个答案是建立一个新的 TCP连接,而题目我理解的是 B 重启之前的TCP连接中, A所在状态。
由于 AB 已经正常建立连接处于 ESTABLISHED 状态,B突然重启,相当于这个 TCP 连接没有正常关闭,B 没有向A FIN关闭信号。等待一段时间后 ,A会去关闭该连接 向B发送一个 FIN,等待 B回复ack同意关闭,此是A的状态 FIN WAIT.
可以讨论一下,我回答的也不全面

@azl397985856
Copy link

因为B会在重启之后进入tcp状态机的listen状态,只要当a重新发送一个数据包(无论是syn包或者是应用数据),b端应该会主动发送一个带rst位的重置包来进行连接重置,所以a应该在syn_sent状态

问题b重启了, b是怎么知道需要发送给a”一个rst位的重置包“呢

@zengyuangai
Copy link

http://crystalwindz.com/unp_note_1/#%E9%9D%9E%E6%AD%A3%E5%B8%B8%E8%BF%9E%E6%8E%A5%E7%BB%88%E6%AD%A2

@azl397985856
Copy link

因为B会在重启之后进入tcp状态机的listen状态,只要当a重新发送一个数据包(无论是syn包或者是应用数据),b端应该会主动发送一个带rst位的重置包来进行连接重置,所以a应该在syn_sent状态

问题b重启了, b是怎么知道需要发送给a”一个rst位的重置包“呢

看了@zengyuangai 的连接, 有了答案。
b和a沟通过程双方有一份数据, b重启之后这份数据没有了,就会发送rst重置。

image

@yygmind yygmind changed the title 关于17题的见解 第 17 题:A、B 机器正常连接后,B 机器突然重启,问 A 此时处于 TCP 什么状态 Apr 26, 2019
@negativeentropy9
Copy link

这么超纲

@zhoubendong
Copy link

牛逼,刚入职的我不适合看这道题

@zhoushoujian
Copy link

对前端来说已经超纲了。
这时候对于A机器来说,如果是刚打开网页的时候,那么浏览器选项卡左上角图标这时候会一直转圈,如果在短时间内B机器重新启动服务了,A机器会连接成功,如果超过了A机器的等待时间,这条请求会挂掉

@xiangnianni
Copy link

菜鸟路过

@mhycy
Copy link

mhycy commented Aug 6, 2019

问题定义

  • A -> B 发起TCP请求,A端为请求侧,B端为服务侧
  • TCP 三次握手已完成
  • TCP 三次握手后双方没有任何数据交互
  • B 在无预警情况下掉线(类似意外掉电重启状态)

问题答案

结论

A侧的TCP链路状态在未发送任何数据的情况下与等待的时间相关,如果在多个超时值范围以内那么状态为<established>;如果触发了某一个超时的情况那么视情况的不同会有不同的改变。

一般情况下不管是KeepAlive超时还是内核超时,只要出现超时,那么必然会抛出异常,只是这个异常截获的时机会因编码方式的差异而有所不同。(同步异步IO,以及有无使用select、poll、epoll等IO多路复用机制)

原因与相关细节

<大前提>

基于IP网络的无状态特征,A侧系统不会在无动作情况下收到任何通知获知到B侧掉线的情况(除非AB是直连状态,那么A可以获知到自己网卡掉线的异常)

在此大前提的基础上,会因为链路环境、SOCKET设定、以及内核相关配置的不同,A侧会在不同的时机获知到B侧无响应的结果,但总归是以异常的形式获得这个结果。

<关于内核对待无数据传递SOCKET的方式>

操作系统有一堆时间超级长的兜底用timeout参数,用于在不同的时候给TCP栈一个异常退出的机会,避免无效连接过多而耗尽系统资源

其中,<TCP KeepAive>特性能让应用层配置一个远小于内核timeout参数的值,用于在这一堆时间超长的兜底参数生效之前,判断链路是否为有效状态。

<关于超时的各个节点>

以下仅讨论三次握手成功之后的兜底情况

TCP链路在建立之后,内核会初始化一个由<nf_conntrack_tcp_timeout_established>参数控制的计时器(这个计时器在Ubuntu 18.04里面长达5天),以防止在未开启TCP KeepAlive的情况下连接因各种原因导致的长时间无动作而过度消耗系统资源,这个计时器会在每次TCP链路活动后重置

TCP正常传输过程中,每一次数据发送之后,必然伴随对端的ACK确认信息。如果对端因为各种原因失去反应(网络链路中断、意外掉电等)这个ACK将永远不会到来,内核在每次发送之后都会重置一个由<nf_conntrack_tcp_timeout_unacknowledged>参数控制的计时器,以防止对端以外断网导致的资源过度消耗。(这个计时器在Ubuntu 18.04里面是300秒/5分钟)

以上两个计时器作为keepalive参数未指定情况下的兜底参数,为内核自保特性,所以事件都很长,建议实际开发与运维中用更为合理的参数覆盖这些数值

<关于链路异常后发生的操作>

A侧在超时退出之后一般会发送一个RST包用于告知对端重置链路,并给应用层一个异常的状态信息,视乎同步IO与异步IO的差异,这个异常获知的时机会有所不同。

B侧重启之后,因为不存有之前A-B之间建立链路相关的信息,这时候收到任何A侧来的数据都会以RST作为响应,以告知A侧链路发生异常

RST的设计用意在于链路发生意料之外的故障时告知链路上的各方释放资源(一般指的是NAT网关与收发两端);FIN的设计是用于在链路正常情况下的正常单向终止与结束。二者不可混淆。

<关于阻塞>

应用层到底层网卡发送的过程中,数据包会经历多个缓冲区,也会经历一到多次的分片操作,阻塞这一结果的发生是具有从底向上传递的特性。

这一过程中有一个需要强调的关键点:socket.send这个操作只是把数据发送到了内核缓冲区,只要数据量不大那么这个调用必然是在拷贝完之后立即返回的。而数据量大的时候,必然会产生阻塞。

在TCP传输中,决定阻塞与否的最终节点,是TCP的可靠传输特性。此特性决定了必须要有ACK数据包回复响应正确接收的数据段范围,内核才会把对应的数据从TCP发送缓冲区中移除,腾出空间让新的数据可以写入进来。

这个过程意味着,只要应用层发送了大于内核缓冲区可容容纳的数据量,那么必然会在应用层出现阻塞,等待ACK的到来,然后把新数据压入缓冲队列,循环往复,直到数据发送完毕。

@mhycy
Copy link

mhycy commented Aug 6, 2019

尝试答一下,欢迎各位拍砖....

@BlingSu
Copy link

BlingSu commented Aug 7, 2019

懵逼状态。

@0scarecrow0
Copy link

一脸懵逼

1 similar comment
@zlccns
Copy link

zlccns commented Dec 4, 2019

一脸懵逼

@yygmind yygmind added the 网络 label Dec 16, 2019
@yygmind
Copy link
Contributor

yygmind commented Dec 16, 2019

如果A 与 B 建立了正常连接后,从未相互发过数据,这个时候 B 突然机器重启,问 A 此时处于 TCP 什么状态?如何消除服务器程序中的这个状态?(超纲题,了解即可)

@yinsw1994
Copy link

所以到底处于什么状态?😓 syn_sent ? FIN WAIT?

@jianglin-wu
Copy link

所以到底是什么状态?

@Dylan0916
Copy link

有没有人话点的版本⋯

@kingda
Copy link

kingda commented Jun 13, 2022

如果开启了keep-alive,中间无数据传输的话,会等到keep-alive过期时间才会断开,断开之前是ESTABLISHED。断开之后是CLOSED
如果没开keep-alive,中间无数据传输的话,一直都是ESTABLISHED。
只要中间有数据传输,会立马重发直到断开。

@rwtfs
Copy link

rwtfs commented Apr 21, 2023

如果 B 机器突然重启,而 A 机器未能检测到此事件,那么此时 A 机器会一直等待 B 机器的响应,因为它并不知道 B 已经宕机。因此,A 机器处于 TCP 的连接状态,也就是说,它的连接状态是未断开的。

如果 A 机器检测到了 B 的重启事件(比如向 B 发送了一个数据包但没有得到响应),那么 A 机器就会认为 B 宕机了,此时 A 机器会关闭这个连接,此时 A 机器处于 TCP 的断开状态。

@JIAFENG123
Copy link

当B机器突然重启时,TCP连接可能会经历一些状态变化。TCP(Transmission Control Protocol)是一种面向连接的协议,连接的建立和维护是通过一系列的状态来管理的。在这种情况下,A机器可能会发现连接状态发生变化,具体取决于TCP实现和配置。

一般而言,TCP连接可能经历以下状态:

ESTABLISHED(已建立):A和B机器之间的连接已经建立,正常通信。

CLOSE_WAIT(等待关闭):B机器可能在重启之前发送了一个关闭请求,A机器已经接收到该请求,并等待进行关闭。

TIME_WAIT(等待时间):A机器在发送最后的确认后进入TIME_WAIT状态,确保在网络中所有的数据包都被正确处理,以避免出现混乱。

CLOSED(已关闭):连接被正常关闭,A机器意识到连接已经关闭。

如果B机器突然重启,A机器可能会注意到连接状态的变化,从ESTABLISHED状态到CLOSED状态。然而,这也取决于具体的TCP实现和网络条件。在一些情况下,A机器可能会在检测到连接中断后立即将状态更改为CLOSED,而在另一些情况下,可能会经历CLOSE_WAIT和TIME_WAIT等状态,以确保连接的正常关闭。

---------- 来自chatgpt3.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests