推荐影片
tcp/ip协议栈技术训练营(一)
tcp/ip协议栈技术训练营(二)
c/c++ linux服务器开发学习地址:c/c++ linux后台服务器高级架构师
断网后,emmm,不知道是一种什么样的体验,但是我坐过飞机,其实还可以,能忍。
好吧,这不是水文,有东西。
连接正常结束:挥手四次告别
1)序号( ):Seq序号,占32位,用于标识从TCP源端发送到目的端的字节流,在发起端发送数据时标记。
2)确认号( ):确认序号,占32位,只有当ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
3)标志(Flags):共6个,分别是URG、ACK、PSH、RST、SYN、FIN。具体含义如下:
ACK:确认序列号有效。
RST:重置连接。
SYN:发起一个新的连接。
FIN:释放连接。
为什么在建立连接时是一起传输,而在连接释放时却是分开传输?
连接建立后,被动服务器端进入阶段结束的“握手”阶段,不需要任何准备。可以直接返回SYN和ACK报文开始建立连接。释放连接时,被动服务器在突然收到主动客户端释放连接的请求时无法立即释放连接,因为还有必要的数据需要处理,所以服务器首先返回ACK确认收到消息,并通过 CLOSE-WAIT 阶段准备好释放连接后,可以返回 FIN 释放连接消息。
为什么客户端在TIME-WAIT阶段等待2MSL?
为了确认服务器是否收到客户端发送的ACK确认消息
当客户端发送最终的 ACK 确认消息时,并不确定服务器是否可以接收到这部分消息。因此,客户端在发送ACK确认消息后,会设置一个时长为2MSL的定时器。MSL 是指 TCP 数据包在传输过程中的最大生命周期。2MSL是服务器发送的FIN报文和客户端发送的ACK确认报文可以保持有效的最长时间。
服务端在1MSL内没有收到客户端发送的ACK确认报文,会再次向客户端发送FIN报文;
如果客户端在2MSL内再次收到服务端的FIN报文,说明服务端由于各种原因没有收到客户端的ACK确认报文。客户端再次向服务器发送ACK确认消息,定时器复位,重新开始2MSL的计时;否则,客户端在2MSL内没有再次收到服务端的FIN报文,说明服务端正常接收到ACK确认报文。,客户端可以进入舞台,完成“四波”。
因此,客户端必须经过 2SML 的 TIME-WAIT 阶段;这就是为什么客户端比服务器晚进入阶段的原因。
毕竟这些都是理论层面的东西,实际场景比这复杂的多。故障模式网络中断
如果网络中断了,就不用提什么是“主动关机”,什么是“FIN”包了。TCP程序也无法感知连接异常,除非路由器发送ICMP报文,表明目的网络或主机不可达;或仅由读取或写入调用返回的错误。
不幸的是,大多数时候情况并非如此。在没有 ICMP 消息的情况下,TCP 程序无法理解连接异常。如果程序在读取调用时被阻塞,那么不幸的是,程序无法从异常中恢复。
如果程序先调用写操作发送数据流,然后阻塞读调用,结果会大不相同。Linux系统的TCP协议栈会不断尝试发送发送缓冲区中的数据。大约重传12次,总时间约9分钟后,协议栈会识别出连接异常。此时,被阻塞的读取调用会返回错误信息。如果此时程序仍在持续向该连接写入数据,则写入操作将立即失败,并向应用程序返回一个信号。
一旦返回此信号,进程将终止。也就是我们常说的,程序崩溃了。
【文章福利】C/C++ Linux服务器架构师需学习资料和群(包括C/C++、Linux、技术、内核、Nginx、MySQL、Redis、、ZK、流媒体、CDN、P2P、K8S、、TCP/ IP、协程、DPDK等)
对端发送FIN报文
这种情况比较常见,至少在我的情况下,一般不会造成太坏的影响,除非同时断开大量连接,会占用大量资源。
如果对端发送FIN报文,可能的场景是对端调用close或显式关闭连接,或者对端的应用程序崩溃,操作系统内核将其发送出去进行清洗。从应用的角度来看,无法区分是哪种情况。
阻塞读操作完成正常接收到的数据读取后,FIN包会通过返回一个EOF来完成通知。此时read调用的返回值为0。这里强调一下,read操作不会在收到FIN包后立即返回。可以这样理解,接收一个FIN包相当于在接收缓冲区中放了一个EOF符号,之前已经在接收缓冲区中的有效数据不受影响。
服务器断开连接
注意,如果我们的速度不够快,导致服务器从睡眠中唤醒并成功发送消息,客户端会正常显示。这时候,我们就留下来等待标准输入。如果不继续通过读写操作对进行读写,就无法感知到已经在服务端关闭的事实。
事无巨细已从服务器断开,看图说话。