TCP三次握手和四次挥手详解

article/2025/9/23 1:27:26

文章目录

      • 三次握手和四次挥手简述
        • 三次握手的目的
        • 三次握手流程详解
        • 半连接队列和全连接队列
        • 四次挥手的目的
        • 四次挥手详解
        • 为什么客户端需要TIME_WAIT状态
        • 为什么挥手比握手多一次
        • 为什么三次挥手不行
        • TCP报文参数释义

三次握手和四次挥手简述

三次握手,即客户端与服务端进行的三次通信。四次挥手,就是客户端和服务端通过四次通信释放连接,也叫连接终止协议。

三次握手的目的

三次握手是为了让客户端和服务端分别确认自己和对方接收和发送消息的能力是正常的。

三次握手流程详解

三次握手

序号报文路径客户端状态报文信息服务器状态目的
第一次握手C->SSYN_SENTSYN=1,seq=xLISTENC:自己发信息正常
S:自己收信息正常,C发消息正常
第二次握手S->CSYN_SENTSYN=1,ACK=1,
seq=y,ack=x+1
SYN_RCVDC:自己收信息正常,S收发信息正常
S: 自己发消息正常,但S不知道C收消息是否正常->需要第三次握手
第三次握手C->SESTABLISHEDACK=1,seq=x+1,ack=y+1ESTABLISHEDS:C接收信息正常
  1. 初始时:客户端处于Closed状态,服务器处于Listen状态;
  2. 第一次握手:客户端发送SYN报文给服务器,初始序列号为x(seq=x), 此时客户端进入SYN_SENT状态;这时,客户端可以知道自己发送能力正常,服务器可以知道自己接收能力正常,客户端发送能力正常。
  3. 第二次握手:服务器通过自己的SYN报文给与客户端确认和响应,服务器进入SYN_RECV状态;这时客户端可以知道服务器收发能力正常,自己收发能力正常;服务器知道自己收发能力正常,但不知道客户端接收能力正常,因此需要第三次握手。服务器发送报文的四个参数具体含义如下:
    1. SYN=1,表示为连接请求报文,也不能携带数据;
    2. seq=y,服务端的序列号为y;
    3. ACK=1,表示确认客户端序列号有效,此时确认号(ack)有值;
    4. ack=seq+1:ack的值为客户端传来的序列号(seq)加1,即ack=x+1;
  4. 第三次握手:客户端收到服务器的SYN+ACK的包,此时客户端处于ESTABLISHED(已确认)状态,表示客户端和服务器都表示同意连接,因此客户端发送一个ACK报文,ack仍为序列号+1,即y+1,初始序列号为x,因此客户端发送的第二次报文,序列号要+1,即x+1;这时服务器可以确认客户端收发能力正常;第三次握手可以携带数据
  • SYN=1或ACK=1表明这是一个连接请求报文
  • SYN=1时的报文不能携带数据,如果可以携带数据的话,如果有人在第一次握手的SYN报文放入很多数据,重复发送大量的这些报文,服务器就会消耗大量内存缓存这些报文,服务器就更容易被攻击。

半连接队列和全连接队列

完成第一次和第二次握手后的TCP连接,讲socket放到半连接队列中;

完成第三次握手后,socket会从半连接队列移到全连接队列,当调用accpet函数时,从全连接队列中返回可用的socket给用户进程。

四次挥手的目的

TCP是全双工的工作模式,因此每个方向都必须单独进行关闭。当一方完成自己的数据发送任务后,就可以发送一个FIN报文来终止这个方向的连接。

全双工:客户端在给服务器端发送消息的同时,服务器端也可以给客户端发送消息;
半双工:客户端可以给服务端发消息,服务器端也可以给客户端发消息,但客户端和服务端不能同时发。

四次挥手详解

四次挥手

  1. 刚开始客户端和服务器端都处于ESTABLISHED状态,假如客户端发起关闭请求;
  2. 第一次挥手:客户端向服务器发送FIN报文(FIN=1,seq=u),发完后进入FIN_WAIT_1状态,即主动关闭TCP连接,不再发送数据,但可以接收服务器发来的报文,等待服务器回复;
  3. 第二次挥手:服务器接到FIN报文后,返回一个ACK报文(ACK=1,ack=u+1,seq=v),表明自己接收到此报文,服务器进入CLOSE_WAIT关闭等待状态,此时客户端就知道服务端接到自己的断开连接请求,进入到FIN_WAIT_2状态,TCP处于半关闭状态,但服务器端可能还有数据要传输。
  4. 第三次挥手:服务器关闭客户端连接,发送FIN报文(FIN=1,seq=w,ack=u+1)给客户端,此时服务器处于LAST_ACK状态,等待客户端回应。
  5. 第四次挥手:客户端收到FIN报文后,发送一个ACK(ACK=1,ack=w+1,seq=u+1)给服务器作为应答,此时客户端处于TIME_WAIT状态,这个状态是为了等待足够的时间以确保TCP接收到连接中断请求的确认
  6. 注意:这时服务器到客户端的TCP连接并未被释放,客户端需要经过等待2MSL(MSL表示一个报文的来回时间)后才会进入CLOSED状态,这样做的目的是确保服务器收到自己的ACK报文,如果在规定时间没有收到客户端发的ACK,那么服务器会重发FIN,客户端再次收到FIN报文,就知道自己的ACK丢了,然后会重发ACK给服务器。服务器收到ACK后,就会关闭连接,处于CLOSE状态了。
  7. 等待2MSL的原因
  • 防⽌客户端最后⼀次发给服务器的确认在⽹络中丢失以⾄于客户端关闭,⽽服务端并未关闭,导致资源的浪费。
  • 等待最⼤的2msl可以让本次连接的所有的⽹络包在链路上消失,以防造成不必要的⼲扰。

如果客户端直接closed,然后⼜向服务端发起了⼀个新连接,我们不能保证这个新连接和刚关闭的连接的端⼝号是不同的。假设新连接和已经关闭的⽼端⼝号是⼀样的,如果前⼀次滞留的某些数据仍然在⽹络中,这些延迟数据会在新连接建⽴后到达服务端,所以socket就认为那个延迟的数据是属于新连接的,数据包就会发⽣混淆。所以客户端要在TIME_WAIT状态等待2倍的MSL,这样保证本次连接的所有数据都从⽹络中消失。

序号报文路径客户端状态报文信息服务器状态
第一次挥手C->SFIN_WAIT_1FIN=1,seq=uESTABLISHED
第二次挥手S->CFIN_WAIT_2ACK=1,seq=v,ack=u+1CLOSE_WAIT
第三次挥手S->CFIN_WAIT_2FIN=1,seq=v,ack=u+1LAST_ACK
第四次挥手C->STIME_WAITACK=1,ack=w+1,seq=u+1CLOSED

为什么客户端需要TIME_WAIT状态

假设最终的ACK丢失,服务器会重发FIN,客户端必须维护TCP状态信息以便可以重发最终的ACK,否则就发送RST(TCP连接出错),结果服务端认为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭),客户端必须进入TIME_WAIT状态,以免可能出现重发ACK的情形

为什么挥手比握手多一次

因为握手的时候并没有数据传输,所以服务端的** SYN 和 ACK 报文可以一起发送**,但是挥手的时候有数据在传输,所以 ACK 和 FIN报文不能同时发送,需要分两步,所以会比握手多一步。

为什么三次挥手不行

因为服务端在接收到FIN, 往往不会立即返回FIN ,必须等到服务端所有的报文都发送完毕了,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。

如果是三次挥手会造成:
如果将服务端的两次挥手合为一次,等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。所有只能第二次握手先发送ACK确认接收到了客户端的数据,等服务器发送完了数据,再发送FIN包进行第三次挥手。

TCP报文参数释义

在三次握手和四次挥手中,报文发送都会携带一些参数,这些参数的具体解释如下:

  • SYN:同步序列号标志位,tcp三次握⼿中,第⼀次会将SYN=1,ACK=0,此时表⽰这是⼀个连接请求报⽂段,对⽅会将SYN=1,ACK=1,表⽰同意连接,连接完成之后将SYN=0

  • FIN:在tcp四次挥⼿时第⼀次将FIN=1,表⽰此报⽂段的发送⽅数据已经发送完毕,这是⼀个释放链接的标志

  • ACK:当ACK=1时,我们的确认序列号ack才有效,当ACK=0时,确认序号ack⽆效,TCP规定:所有建⽴连接的ACK必须全部置为1

  • 序号(seq):占 32位4 个字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。例如:我们的seq = 201,携带的数据有100,那么最后⼀个字节的序号就为300,那么下⼀个报⽂段就应该从301开始.

  • 确认号(ack):占 32位4 个字节,期望收到对方下个报文段的第一个数据字节的序号。当标志位ACK值为1时,才能产生有效的确认号ack。并且:ack=seq+1;

  • RST:当RST=1时,表明TCP连接出现严重错误,此时必须释放连接,之后重新连接,⼜叫重置位.

  • URG:紧急指针标志位,当URG=1时,表明紧急指针字段有效.它告诉系统中有紧急数据,应当尽快传送,这时不会按照原来的排队序列来传送.⽽会将紧急数据插⼊到本报⽂段数据的最前⾯

  • PSH:推送操作,提示接收端应用程序立即从TCP缓冲区把数据读走

ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号


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

相关文章

三次握手,四次挥手,为什么是三次握手四次挥手

三次握手 两次握手(情况1) 两次握手(情况2) OK,下面正经地来回答下这个问题,要搞清楚这个问题,首先得了解TCP究竟是如何保证可靠传输的。 PS:TCP协议中,主动发起请求的一…

TCP三次握手四次断开

转载地址:www.51niux.com IP协议是网络层的主要协议,为上层传输层提供无连接、无状态、不可靠的服务。优点是简单高效。无状态是指各个IP报文是独立传送的,不同步传输状态的信息,所以容易发生重复和乱序的情况。不可靠是指IP协议…

有关三次握手,四次挥手的超详细总结!!!

有关三次握手,四次挥手的超详细总结!!! 我们先来看一下三次握手和四次挥手的示意图: 图示为三次握手。 图示为四次挥手。 一、三次握手和四次挥手的过程: 三次握手: TCP建立连接的过程我们…

TCP四次挥手详解

在开始之前可以先了解一下 TCP三次握手 TCP四次挥手过程和状态变迁 为什么挥手需要四次? 为什么TIME_WAIT等待的时间是2MSL? 等待2MSL的意义 TIME_WAIT状态过多有什么危害? 如何解决TIME_WAIT状态过多? TCP四次挥手过程和…

C++ map遍历

C map遍历 #include <iostream> #include <map>using namespace std;int main() {map<int, int> _map;_map[0] 1;_map[1] 2;_map[10] 10;map<int, int>::iterator iter;iter _map.begin();while(iter ! _map.end()) {cout << iter->firs…

C++ map遍历(简单易记忆)

C中map遍历有两种方法&#xff1a; 第一种&#xff0c;使用迭代器&#xff0c;while循环 #include <iostream> #include <map> using namespace std; int main() {map<int,int> p;p[0] 1;p[1] 2;p[3] 4;map<int,int>::iterator it p.begin();whi…

Java中Map的4种遍历方式

原标题&#xff1a;Java中Map的4种遍历方式 第一种方式&#xff1a;这是平常用的最多也最可取的一种遍历方式。 for (Map.Entry entry : map.entrySet()) { System.out.println(“key” entry.getKey() “,value” entry.getValue()); 第二种方式&#xff1a;如果只需要…

HashMap的几种遍历方式及循环删除

目录 1. 前言2. HashMap 的遍历方式2.1. 迭代器 EntrySet2.2. 迭代器 KeySet2.3. ForEach EntrySet2.4. ForEach KeySet2.5. Lambda 表达式2.6. Streams API 3. 循环删除3.1. 迭代器 Iterator 方式3.2. ForEach 循环方式3.3. Lambda 表达式3.3.1. Lambda 删除的正确方式 3.4. S…

Map遍历四种方式及其效率

目录 1、Map介绍 2、Map数据结构及扩容 2.1、数组&#xff1a;寻址容易&#xff0c;插入和删除元素困难 2.2、链表&#xff1a;寻址困难&#xff0c;插入和删除元素容易 2.3、Map数组长度默认16&#xff0c;扩容负载因子为0.75 3、Map遍历4种方式及其效率 3.1、方式一&a…

前端map循环遍历使用

map定义 Array.map() ⽅法返回⼀个新数组&#xff0c;数组中的元素为原始数组元素调⽤函数处理后的值&#xff0c;同时不会改变原来的数组 var newArra[1,3,6,10,44]; var newArraysnewArra.map((index) > { return index*index }) console.log(newArra); //[1,3,6,10,44]…

map forEach for in 循环遍历

1.map遍历数组对象 var map [{ key : "百度", value : "李彦宏" },{key : "阿里巴巴", value : "马云" },]; for (var key in map) { console.log(map[key]); } 2.forEach遍历数组 forEach()方法需要一个函数作为参数&…

Map的循环遍历,修改,删除

预制数据 Map<String, String> map new HashMap<>();map.put("aaa","123");map.put("bbb","123");map.put("CCC","123"); 只循环key ---> map.keyset() for (String key : map.keySet()) {Syst…

Java遍历Map五种方法

一、Map集合遍历日常开发最常使用&#xff0c;简单总结五种方法差异。 ①、IteratorentrySet写法【推荐JDK8以下】&#xff0c;Map.Entry是Map接口的内部接口&#xff0c;获取迭代器&#xff0c;然后依次取出每个迭代器里面的Map.Entry Iterator<Map.Entry<Integer,Strin…

443端口被占用怎么办

前段时间配置SDN的时候装了虚拟机&#xff0c;然后最近打开Steam插件访问社区的时候发现跳出错误&#xff0c;443端口被占用&#xff0c;一脸懵逼&#xff0c;后来查找资料才发现原来是虚拟机的vmware-hosted进程占用了443端口 可以通过右键计算机——服务和应用程序——服务—…

当443端口被占用时如何解决?

有时候我们的一些软件需要443端口才能运行&#xff0c;但是443被占用了&#xff0c;应当怎么结束呢&#xff1f; 1&#xff0c;打开CMD&#xff0c;输入netstat -ano回车&#xff0c;如图&#xff1a; 可以看到某程序正在占用443端口&#xff08;左边一列0.0.0.0:443,0.0.0.0代…

steamcom启动服务:443端口被占用,请关闭占用该端口的进程后再点击启动服务!

steamcom启动服务&#xff1a;443端口被占用&#xff0c;请关闭占用该端口的进程后再点击启动服务! 最近想玩玩游戏放松放松&#xff0c;但是steam有点问题&#xff0c;于是下了个插件steamcom,但在启动服务时出现了错误。 过程结果及解决方案 1、下载压缩文件并解压 2、运…

解除445端口的占用

有很多时候,我们测试的时候,会需要用到445端口。而windows默认该端口是开放的,那么我们如何关闭该端口的占用状态呢? 不是通过防火墙阻止445端口的连接来关闭,这种是阻止外部连接,但是本地445端口还是开放的。 我们这里讲的是关闭占用445端口的服务! 关闭Server服务 …

steamcommunity本地反代443端口/80端口被占用解决办法

参考了这两篇文章&#xff1a; https://www.cnblogs.com/chihirotan/p/5785339.html https://blog.csdn.net/mituan1234567/article/details/51085580 太长不看版&#xff1a; 我的电脑最近几天装了VMware&#xff0c;VMware占用了443端口导致steamcommunity无法启动。解决办…

端口被占用但实际端口没被占用

【问题描述】 启动nacos、sentinel时报端口被占用&#xff0c;使用netstat -aon|findstr 8080查找端口使用情况却发现该端口并没有被使用 【问题解决】 原出处&#xff1a;win10系统遇到了一个十分诡异的情况&#xff0c;端口明明没被占用却老是提示端口已占用-CSDN社区 原…

端口被占用如何关闭

在开发的时候有些时候会发现端口冲突的问题&#xff0c;比如最常使用的8080端口为例&#xff1a; 那么如何查询那个进程使用该端口并进行关闭呢&#xff1f; 首先进入命令行窗口输入下方的命令查看所有的端口占用情况 >netstat -ano上面一步不是必须的&#xff0c;然后输入…