TCP 握手没成功怎么办?

article/2025/10/1 18:38:08

大家好,我是小林。

之前收到个读者的问题,对于 TCP 三次握手和四次挥手的一些疑问:

  • 第一次握手,如果客户端发送的SYN一直都传不到被服务器,那么客户端是一直重发SYN到永久吗?客户端停止重发SYN的时机是什么?

  • 第三次握手,如果服务器永远不会收到ACK,服务器就永远都留在 Syn-Recv 状态了吗?退出此状态的时机是什么?

  • 第三次挥手,如果客户端永远收不到 FIN,ACK,客户端永远停留在 Fin-Wait-2状态了吗?退出此状态时机是什么时候呢?

  • 第四次挥手,如果服务器永远收不到 ACK,服务器永远停留在 Last-Ack 状态了吗?退出此状态的时机是什么呢?

  • 如果客户端 在 2SML内依旧没收到 FIN,ACK,会关闭链接吗?服务器那边怎么办呢,是怎么关闭链接的呢?

可以看到,这些问题都是关于 TCP 是如何处理这些异常场景的,我们在学 TCP 连接建立和断开的时候,总是以为这些过程能如期完成。

可惜理想很丰满,现实很骨感,事实预料呀

TCP 当然不傻,对以上这些异常场景都是有做处理的。

这次就针对读者问的这一系列问题,来详细说说 TCP 是怎么处理这些异常的?

这些异常场景共分为两大类,第一类是 TCP 三次握手期间的异常,第二类是 TCP 四次挥手期间的异常。

TCP 三次握手期间的异常

我们先来看看 TCP 三次握手的过程。

image.png

第一次握手丢失了,会发生什么?

当客户端想和服务端建立 TCP 连接的时候,首先第一个发的就是 SYN 报文,然后进入到 SYN_SENT 状态。

在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发超时重传机制。

不同版本的操作系统可能超时时间不同,有的 1 秒的,也有 3 秒的,这个超时时间是写死在内核里的,如果想要更改则需要重新编译内核,比较麻烦。

当客户端在 1 秒后没收到服务端的 SYN-ACK 报文后,客户端就会重发 SYN 报文,那到底重发几次呢?

在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries 内核参数控制,这个参数是可以自定义的,默认值一般是 5。

通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍

当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。

所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。

第二次握手丢失了,会发生什么?

当服务端收到客户端的第一次握手后,就会回 SYN-ACK 报文给客户端,这个就是第二次握手,此时服务端会进入 SYN_RCVD 状态。

第二次握手的 SYN-ACK 报文其实有两个目的 :

  • 第二次握手里的 ACK, 是对第一次握手的确认报文;
  • 第二次握手里的 SYN,是服务端发起建立 TCP 连接的报文;

所以,如果第二次握手丢了,就会发送比较有意思的事情,具体会怎么样呢?

因为第二次握手报文里是包含对客户端的第一次握手的 ACK 确认报文,所以,如果客户端迟迟没有收到第二次握手,那么客户端就觉得可能自己的 SYN 报文(第一次握手)丢失了,于是客户端就会触发超时重传机制,重传 SYN 报文

然后,因为第二次握手中包含服务端的 SYN 报文,所以当客户端收到后,需要给服务端发送 ACK 确认报文(第三次握手),服务端才会认为该 SYN 报文被客户端收到了。

那么,如果第二次握手丢失了,服务端就收不到第三次握手,于是服务端这边会触发超时重传机制,重传 SYN-ACK 报文

在 Linux 下,SYN-ACK 报文的最大重传次数由 tcp_synack_retries 内核参数决定,默认值是 5。

因此,当第二次握手丢失了,客户端和服务端都会重传:

  • 客户端会重传 SYN 报文,也就是第一次握手,最大重传次数由 tcp_syn_retries 内核参数决定。;
  • 服务端会重传 SYN-AKC 报文,也就是第二次握手,最大重传次数由 tcp_synack_retries 内核参数决定。

第三次握手丢失了,会发生什么?

客户端收到服务端的 SYN-ACK 报文后,就会给服务端回一个 ACK 报文,也就是第三次握手,此时客户端状态进入到 ESTABLISH 状态。

因为这个第三次握手的 ACK 是对第二次握手的 SYN 的确认报文,所以当第三次握手丢失了,如果服务端那一方迟迟收不到这个确认报文,就会触发超时重传机制,重传 SYN-ACK 报文,直到收到第三次握手,或者达到最大重传次数。

注意,ACK 报文是不会有重传的,当 ACK 丢失了,就由对方重传对应的报文

TCP 四次挥手期间的异常

我们再来看看 TCP 四次挥手的过程。

image.png

第一次挥手丢失了,会发生什么?

当客户端(主动关闭方)调用 close 函数后,就会向服务端发送 FIN 报文,试图与服务端断开连接,此时客户端的连接进入到 FIN_WAIT_1 状态。

正常情况下,如果能及时收到服务端(被动关闭方)的 ACK,则会很快变为 FIN_WAIT2 状态。

如果第一次挥手丢失了,那么客户端迟迟收不到被动方的 ACK 的话,也就会触发超时重传机制,重传 FIN 报文,重发次数由 tcp_orphan_retries 参数控制。

当客户端重传 FIN 报文的次数超过 tcp_orphan_retries 后,就不再发送 FIN 报文,直接进入到 close 状态。

第二次挥手丢失了,会发生什么?

当服务端收到客户端的第一次挥手后,就会先回一个 ACK 确认报文,此时服务端的连接进入到 CLOSE_WAIT 状态。

在前面我们也提了,ACK 报文是不会重传的,所以如果服务端的第二次挥手丢失了,客户端就会触发超时重传机制,重传 FIN 报文,直到收到服务端的第二次挥手,或者达到最大的重传次数。

这里提一下,当客户端收到第二次挥手,也就是收到服务端发送的 ACK 报文后,客户端就会处于 FIN_WAIT2 状态,在这个状态需要等服务端发送第三次挥手,也就是服务端的 FIN 报文。

对于 close 函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2 状态不可以持续太久,而 tcp_fin_timeout 控制了这个状态下连接的持续时长,默认值是 60 秒。

这意味着对于调用 close 关闭的连接,如果在 60 秒后还没有收到 FIN 报文,客户端(主动关闭方)的连接就会直接关闭。

第三次挥手丢失了,会发生什么?

当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于 CLOSE_WAIT 状态,顾名思义,它表示等待应用进程调用 close 函数关闭连接。

此时,内核是没有权利替代进程关闭连接,必须由进程主动调用 close 函数来触发服务端发送 FIN 报文。

服务端处于 CLOSE_WAIT 状态时,调用了 close 函数,内核就会发出 FIN 报文,同时连接进入 LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。

如果迟迟收不到这个 ACK,服务端就会重发 FIN 报文,重发次数仍然由 tcp_orphan_retries 参数控制,这与客户端重发 FIN 报文的重传次数控制方式是一样的。

第四次挥手丢失了,会发生什么?

当客户端收到服务端的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入 TIME_WAIT 状态。

在 Linux 系统,TIME_WAIT 状态会持续 60 秒后才会进入关闭状态。

然后,服务端(被动关闭方)没有收到 ACK 报文前,还是处于 LAST_ACK 状态。

如果第四次挥手的 ACK 报文没有到达服务端,服务端就会重发 FIN 报文,重发次数仍然由前面介绍过的 tcp_orphan_retries 参数控制。


是吧,TCP 聪明着很!


http://chatgpt.dhexx.cn/article/SAEygNMl.shtml

相关文章

深入理解TCP三次握手

一、TCP 包头格式 首先,TCP报文是TCP层传输的数据单元,也称为报文段,下面就是TCP包头格式: 接下来我们来看看每个字段的含义: 源端口和端口字号: TCP源端口:源计算机上应用程序端的端口号&…

TCP的三次握手及四次挥手详解

三次握手 三次握手过程: (1)第一次握手:Client将标志位SYN置为1(表示要发起一个连接),随机产生一个值seqJ,并将该数据包发送给Server,Client进入SYN_SENT状态&#xff0c…

C语言中main函数参数使用

在C99标准中定义main函数两种正确的写法 int main(void); int main(int argc, char* argv[]);常见的不标准写法 void main() main()这里主要说明带参数的main函数如何使用 int main(int argc, char* argv[]) {int i;for (i0; i<argc; i)printf("%d: %s\r\n", i…

C语言main函数参数、返回值

C语言main函数返回值&#xff1a; main函数的返回值&#xff0c;用于说明程序的退出状态。如果返回0&#xff0c;则代表程序正常退出&#xff1b;返回其他数字的含义则由系统决定&#xff0c;通常&#xff0c;返回非零代表程序异常退出&#xff0c;即使程序运行结果正确也仍需修…

main 函数的参数说明

C/C语言中的main函数&#xff0c;经常带有参数argc&#xff0c;argv&#xff0c;如下&#xff1a; int main(int argc, char** argv)int main(int argc, char* argv[])这两个参数的作用是什么呢&#xff1f;argc 是指命令行输入参数的个数&#xff0c;argv存储了所有的命令行参…

C++ main函数及main函数的参数

C main函数及main函数的参数 1、main函数的几种形式 int main() int main(int argc) int main(int argc,char** argv)//int main(int argc,char* argv[])2、argc表示命令行参数的个数、argv表示命令行参数的值 &#xff08;1&#xff09;写个小代码&#xff0c;用命令行运行…

带参数的main函数

支持C语言的系统允许main函数有两个参数 int main(int argc,char *argv[]){//argc表示从命令行传入的参数的个数&#xff1b;//argv表示从命令行传入的字符串数组&#xff1b; } 回显命令行参数 #include<stdio.h> int main(int argc,char *argv[]){int i;for(i0;i<…

main主函数参数解析

默认的main函数参数 int main(int argc, char *argv[]) {// 主函数的代码逻辑return 0; }其中&#xff0c;int 是主函数的返回值类型&#xff0c;主函数执行完后会返回一个整数值给操作系统&#xff0c;通常返回值为 0 表示程序正常结束&#xff0c;非 0 的返回值表示程序运行…

C语言main函数参数

常见的C语言的main函数都是不带参数的。因此main 后的括号都是空括号。实际上&#xff0c;main函数可以带参数&#xff0c;这个参数可以认为是main函数的形式参数。C语言规定main函数的参数只能有两个&#xff0c;习惯上这两个参数写为argc和argv。因此&#xff0c;main函数的函…

【C语言】main函数的参数

我们先看看主函数main的参数列表 #include <stdio.h>int main(int argc, char *argv[]) {return 0; }argc是一个整型变量&#xff0c;存储的是主函数的参数个数argv[]是一个字符型指针数组&#xff0c;其中存储的是主函数的参数字符串&#xff0c;是一个参数列表 注意:…

C++中main函数的参数

C中main函数的参数 问题 对于一个C/CPP文件来说&#xff0c;一般都有main函数&#xff0c;一个程序的执行一定会是先从main函数这个入口开始&#xff0c;一个最基本的程序框架大概长这样: #include <stdio.h> int main() {return 0; }但是&#xff0c;我们有时候看到的…

C++ main函数的参数

int main(int argc, char* argv[]) 那main函数的argc和argv参数是什么意思呢&#xff1f; 这两个参数主要是用来保存程序运行时传递给main函数的命令行参数的。 argc&#xff1a;是argument count 的缩写&#xff0c;保存运行时传递给main函数的参数个数。 argv&#xff1a;是…

main函数的参数的含义

转载自&#xff1a;点击链接 链接2 加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 最近学习服务器网络编程&#xff0c;遇到了一个问题&#xff0c;main函数的参数&#xff0c;特意整理资料记录之&#xff01;&#…

C++main函数及参数

转载自&#xff1a;https://www.cnblogs.com/carsonzhu/p/5276317.html C main()函数及其参数 1、首先&#xff0c;想想C/C在main函数之前和之后会做些什么&#xff1f; 我们看看底层的汇编代码&#xff1a; __start: :      init stack;      init heap;     …

带参数的的main函数

在main()函数中允许带2个参数&#xff0c;一个为整型argc,另一个是指向字符型的指针数组argv[]。格式&#xff1a; int main(int argc,char *argv[])   其中整型argc表示命令行中字符串的个数&#xff0c;指针数组argv[]指向命令行中的各个字符串。这两个参数可以用任何合法…

C语言main函数参数详解

main函数参数 一共有三个&#xff1a; 1.int argc 整型变量 2.char *argv[] 字符指针的数组&#xff0c;通俗一点就是字符串数组&#xff0c;每个元素都是字符串 3.char *envp[] 字符串数组 这三个东西再怎么神秘&#xff0c;也只是函数参数&#xff0c;只不过是main函数的参数…

main函数的参数

main函数可以不带参数,也可以带参数&#xff0c;这个参数可以认为是 main函数的形式参数。C语言规定main函数的参数只能有两个&#xff0c;习惯上这两个参数写为argc和argv。因此&#xff0c;main函数的函数头可写为&#xff1a; main (argc,argv) C语言还规定argc(第一个形参…

main函数的参数解析

下面我们看一下main函数的原型&#xff1a; int main(int argc char* argv[],char* envp) {program-statements } 其实main函数有三个参数&#xff1a; 第一个参数&#xff1a;argc是个整型变量&#xff0c;表示命令行参数的个数&#xff08;含第一个参数&#xff09;。第二…

main 函数参数

一、main函数的参数 main函数有三个参数&#xff0c;argc、argv和envp&#xff0c;它的标准写法如下&#xff1a; int main(int argc,char *argv[],char *envp[])** int argc&#xff0c;存放了命令行参数的个数。 char argv[]&#xff0c;是个字符串的数组&#xff0c;每个元…

[C/C++基础知识] main函数的参数argc和argv

该篇文章主要是关于C\C语言最基础的main函数的参数知识,是学习C或C语言都必备的知识点.不知道你是否知道该知识&#xff1f;希望对大家有所帮助.一.main()函数参数通常我们在写主函数时都是void main()或int main() {..return 0;},但ANSI-C(美国国家标准协会,C的第一个标准ANSI…