TCP三次握手四次断开

article/2025/9/23 1:51:43

转载地址:www.51niux.com

IP协议是网络层的主要协议,为上层传输层提供无连接、无状态、不可靠的服务。优点是简单高效。无状态是指各个IP报文是独立传送的,不同步传输状态的信息,所以容易发生重复和乱序的情况。不可靠是指IP协议不能保证数据报一定能被送达,可靠性主要是通过传输层的TCP协议来保证的。
TCP协议是面向连接的协议,是工作在传输层的协议。TCP协议通过三次握手、四次挥手、流量控制、拥塞控制、超时重传、确认报文等机制来保证可靠性。

IP报文首部

在这里插入图片描述
第一层:前四个字节是版本号(IPv4或IPv6),下面后四个字节是包头的首部长度,首部长度(HLEN)这个四位字段定义了数据报首部的总长度,首部长度是可变的(在20-60字节之间)在没有选项时,首部长度是20字节,且这个字段的值是5(5*4=20)。后面8位是服务类型用来获得更好的服务,在旧标准中叫做服务类型,但实际上一直未被使用过.1998 年这个字段改名为区分服务.只有在使用区分服务(DiffServ)时,这个字段才起作用.一般的情况下都不使用这个字段,后面的16是报文的总长度,总长度-首部长度,就是数据长度

第二层:段标识符(IP报文太长需要分片,就需要段标识来标示),标志占三位占3位,目前只有前两位有意义,中间位是DF标示,不能分片只有当 DF=0 时才允许分片(如果不允许分片发送不出去就返回错误信息了),最后一位是MF标示更多的分片,MF=1 表示后面“还有分片”。MF=0 表示最后一个分片。最后的是用来定义片偏移量,占12位,指较长的分组在分片后某片在原分组中的相对位置.片偏移以 8 个字节为偏移单位,越小越在前面。

第三层:生存时间,每经过一个网关设备TTL就-1,减到0还没有到设备就丢弃,TTL 字段是由发送端初始设置一个8 bit字段.推荐的初始值由分配数字 RFC 指定,当前值为 64.发送 ICMP 回显应答时经常把 TTL 设为最大值 255。后8个字节标示协议类型,指出此数据报携带的数据使用何种协议以便目的主机的IP层将数据部分上交给哪个处理过程, 1表示为 ICMP 协议, 2表示为 IGMP 协议, 6表示为 TCP 协议, 17表示为 UDP 协议。最后是首部校验和,存放首部的校验码。

第四层:源IP

第五层:目标IP

第六层:标记是什么协议什么进程进行的通信,端口号的范围是0-65535,linux主机一般0-1023端口是系统占用的,大于5000的端口才允许客户端随意使用呢,linux没打开一个端口就是打开了一个套接字文件。

第七层:数据。这是在数据报中要传输的数据。它是一个完整的较高层报文或报文的一个分片。

TCP报文首部

在这里插入图片描述
第一层:发送方的端口号,接收方的端口号

第二层:32位序列号:也就是我们tcp三次握手中的seq,表示的是我们tcp数据段发送的第一个字节的序号,范围[0,2^32 - 1]

第三层:32位确认序列号:也就是ack,序列号+1就是确认号。

第四层:首部长度,后面是6个保留位置,URG标示紧急指针(URG为1表示有效,0表示无效)它告诉系统中有紧急数据,应当尽快传送,这时不会按照原来的排队序列来传送.而会将紧急数据插入到本报文段数据的最前面.ACK用于说明确认号是否有效,当ACK=1时,我们的确认序列号ack才有效,当ACK=0时,确认序号ack无效,TCP规定:所有建立连接的ACK必须全部置为1.PSH推送,一旦发生推送报文立即送往内核,RST表示重置,当RST=1时,表明TCP连接出现严重错误,此时必须释放连接,之后重新连接,又叫重置位。SYN发送同步请求。FYN断开连接。最后是窗口大小。

第五层:TCP的校验码,可选长度的可选数据

第六层:其最大长度可根据TCP首部长度进行推算。TCP首部长度用4位表示,那么选项部分最长为:(2^4-1)*4-20=40字节。选项部分的应用:MSS最大报文段长度(Maxium Segment Size),窗口扩大选项(Windows Scaling),SACK选择确认项(Selective Acknowledgements),时间戳(Timestamps),NOP(NO-Operation)

第七层:数据。

字段含义
URG紧急指针是否有效。为1,表示某一位需要被优先处理
ACK确认号是否有效,一般置为1。
PSH提示接收端应用程序立即从TCP缓冲区把数据读走。
RST对方要求重新建立连接,复位。
SYN请求建立连接,并在其序列号的字段进行序列号的初始值设定。建立连接,设置为1
FIN希望断开连接。

TCP三次握手

在这里插入图片描述
第一次握手:
客户端发送一个TCP的SYN标志位置1的包,ACK=0,TCP规定SYN=1时不能携带数据,当SYN=1而ACK=0时,表明这是一个连接请求报文但要消耗一个序号,因此声明自己的序号是seq=i。Seq:序号,4字节,范围为032—132,共4284967296,达到时重新开始计算。Clinet进入SYN_SENT状态,等待Server确认。所以最终客户端发送的报文中包含SYN=1,ACK=0,seq=i(i为一个随机数)。

第二次握手:
服务器发回确认包(ACK)应答。即SYN=1 ACK=1,因为建立连接,则应在响应报文中使SYN=1和ACK=1。seq=j(产生的随机包序号),ack=i+1(确认客户端序号有效)。所以发送给客户端的包里面包含:SYN=1,ACK=1,seq=j,ack=i+1,此时服务器进入SYN_RECV状态。

第三次握手:
客户端收到返回的包进行确认,检查ack是否为i+1(也就是是不是自己第一次发起请求时候产生的随机序号+1),ACK是否为1,如果正确则将标志位ACK置为1,并将ack=j+1(即在服务器序号的基础上加1),seq=i+1(也就是最早的seq序号)发送给服务器端,服务器收到后确认seq=i+i,ACK=1,ack=j+1,服务端验证没有问题随建立连接,并开始打开端口为客户端发送数据。客户端服务端进入ESTABLISHED状态,完成三次握手。

TCP四次断开

在这里插入图片描述
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
不管是客户端还是服务器端都可以调用close之类的函数主动终止一个连接。这里以客户端主动断开连接举例。

第一次握手:
客户端调用close函数,主动发送一个FIN报文给服务器端,用来关闭客户端到服务器的数据传送,此时客户端进入TIME_WAIT1状态。

FIN报文也可能附加用户数据。发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送)但是还可以接收数据。

当调用recv时,如果返回0就表示对端关闭。这个时候通常被关闭端也调用close,然后TCP层发送FIN,继续完成四次握手。如果被关闭端不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。

FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

第二次握手:
服务器收到这个FIN,它发回一个ACK,确认号为收到的序号加1,和SYN一样,一个FIN将占用一个序号,此时服务器进入CLOSE_WAIT状态,客户端进入TIME_WAIT2状态。

第三次握手:
当服务器端也没有要传送的数据时,服务器关闭与客户端的连接,发送一个FIN给客户端A,服务器进入LAST_ACK状态。

第四次握手:
客户端发回ACK报文确认,并将确认号设置为收到序号加1,服务器收到报文并确认成功,服务器端进入CLOSED状态,客户端进入TIME_WAIT(表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。),等待2MSL–120s-240s(1、确保连接可靠地关闭; 即防止最后一个ACK丢失。2、避免产生套接字混淆(同一个端口对应多个套接字),没有收到任何回复,则证明server端已正常关闭,客户端也就关闭了。

常见面试题

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

参考链接

https://blog.csdn.net/soullsj/article/details/80304124
https://blog.csdn.net/qq_38950316/article/details/81087809


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

相关文章

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

有关三次握手,四次挥手的超详细总结!!! 我们先来看一下三次握手和四次挥手的示意图: 图示为三次握手。 图示为四次挥手。 一、三次握手和四次挥手的过程: 三次握手: 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;然后输入…

VMWare Workstation 443端口被占用

问题说明及检测&#xff1a; 今天安装svn的时候提示端口占用&#xff0c; cmd –>> netstat -aon | findstr “443” 查看到进程的pid为3312 ctrl shift esc 选择服务&#xff0c;查看到进程名&#xff08;点下PID可排序&#xff09; 这个问题是处在VMware上&…

443端口占用问题

起因 科学上网有问题 呗计算机积极拒绝了 俺也不晓得是啥问题&#xff0c;就先去排查了一些问题 下面是两个链接 给我很大帮助 cmd来查看和排出433是谁在占用 VMware占用443的解决路径 查看433占用 使用cmd netstat -ano|findstr “443” 得到如下结果 然后使用 tasklist|…