TCP的三次握手与四次挥手过程解析


1.TCP协议

1.TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,用于在计算机网络中传输数据。TCP提供了可靠的、有序的数据传输,并且具备拥塞控制和流量控制的能力。

2.TCP通过三次握手建立连接,确保通信双方的可靠性和完整性。在数据传输过程中,TCP将数据分割成以报文段为单位的数据块,并添加首部信息,包括源端口、目标端口、序列号、确认号、窗口大小等。接收方收到数据后会发送确认应答,以保证数据的可靠传输。

3.TCP协议被广泛应用于互联网中各种应用层协议,如HTTP、FTP、SMTP等,以及各种基于传输层的应用程序。它是一种可靠性较高、适用于大多数应用场景的传输协议。

2.报文格式说明

TCP的数据帧格式请添加图片描述

1.源端口号

源端⼝号表示报⽂的发送端⼝,占16位。源端⼝和源IP地址组合起来,可以标识报⽂的发送地址

2.目的端口号

目的的端口号,表示报文的接受端口,占16位,目的端口和⽬的IP地址相结合,可以标识报⽂的接收地址。

TCP协议是基于IP协议的基础上传输的, TCP报⽂中的源端⼝号+源IP,与TCP报⽂中的⽬的端⼝
号+⽬的IP⼀起,组合起来唯⼀性的确定⼀条TCP连接。

3.序号

TCP传输过程中,在发送端出的字节流中,传输报⽂中的数据部分的每⼀个字节都有它的编号。序号(Sequence Number)占32位,发起⽅发送数据时,都需要标记序号。

序号(Sequence Number)的语义与SYN控制标志(Control Bits)的值有关。根据控制标志Control Bits)中的SYN是否为1,序号(Sequence Number)表达不同的含义:
(1)当SYN = 1时,当前为连接建⽴阶段,此时的序号为初始序号ISN((Initial Sequence Number),通过算法来随机⽣成序号;
(2)当SYN = 0时在数据传输正式开始时,第⼀个报⽂的序号为 ISN + 1,后⾯的报⽂的序号,为前⼀个报⽂的SN值+TCP报⽂的净荷字节数(不包含TCP头)。⽐如,如果发送端发送的⼀个TCP帧的净荷为12byte,序号为5,则发送端接着发送的下⼀个数据包的时候,序号的值应该设置为5+12=17。

在数据传输过程中, TCP协议通过序号(Sequence Number)对上层提供有序的数据流。发送端可以⽤序号来跟踪发送的数据量;接收端可以⽤序号识别出重复接收到的TCP包,从⽽丢弃重复包;对于乱序的数据包,接收端也可以依靠序号对其进⾏排序

4.确认序号

确认序号(Acknowledgment Number)标识了报⽂接收端期望接收的字节序列。如果设置了ACK控制位,确认序号的值表示⼀个准备接收的包的序列码,注意,它所指向的是准备接收的包,也就是下⼀个期望接收的包的序列码。

举个例⼦,假设发送端(如Client)发送3个净荷为1000byte、起始SN序号为1的数据包给Server服务端, Server每收到⼀个包之后,需要回复⼀个ACK响应确认数据包给Client。ACK响应数据包的ACK Number值,为每个Client包的为SN+包净荷,既表示Server已经确认收到的字节数,还表示期望接收到的下⼀个Client发送包的SN序号,具体的ACK值如下图左边的正常传输部分所示。

在这里插入图片描述

在上图的左边部分, Server第1个ACK包的ACK Number值为1001,是通过Client第1个包的SN+包 净荷=1+1000计算得到,表示期望第2个Client包的SN序号为1001; Server第2个ACK包的ACKNumber值为2001,为Client第2个包的SN+包净荷=2001,表示期望第3个Server包的SN为2001,以此类推。

如果发⽣错误,假设Server在处理Client的第⼆个发送包异常, Server仍然回复⼀个ACKNumber值为1001的确认包,则Client的第⼆个数据包需要重复发送,具体的ACK值如上图右边的正常传输部分所示。

只有控制标志的ACK标志为1时,数据帧中的确认序号ACK Number才有效。 TCP协议规定,连接建⽴后,所有发送的报⽂的ACK必须为1,也就是建⽴连接后,所有报⽂的确认序号有效。如果是SYN类型的报⽂,其ACK标志为0,故没有确认序号。

5.头部长度

该字段占⽤4位,⽤来表示TCP报⽂⾸部的⻓度,单位是4bit位。其值所表示的并不是字节数,⽽是头部的所含有的32bit的数⽬(或者倍数),或者4个字节的倍数,所以TCP头部最多可以有60字节
(4*15=60)。没有任何选项字段的TCP头部⻓度为20字节,所以其头部⻓度为5,可以通过20/4=5计算得到。

6.保留字段

头部⻓度后⾯预留的字段⻓度为6位,作为保留字段,暂时没有什么⽤处。

7.控制标志

共6个bit位,具体的标志位为: URG、 ACK、 PSH、 RST、SYN、 FIN。 6个标志位的说明,如下表所示
在这里插入图片描述

在连接建⽴的三次握⼿过程中,若只是单个SYN置位,表示的只是建⽴连接请求。如果SYN和ACK同时置位为1,表示的建⽴连接之后的响应。

8.窗口大小

⻓度为16位,共2个字节。此字段⽤来进⾏流量控制。流量控制的单位为字节数,这个值是本端期望⼀次接收的字节数。

9.检验和

⻓度为16位,共2个字节。对整个TCP报⽂段,即TCP头部和TCP数据进⾏校验和计算,接收端⽤于对收到的数据包进⾏验证。

10.紧急指针

⻓度为16位, 2个字节。它是⼀个偏移量,和SN序号值相加表示紧急数据最后⼀个字节的序号。

以上⼗项内容是TCP报⽂⾸部必须的字段,也称固有字段,⻓度为20个字节。接下来是TCP报⽂的可选项和填充部分。

11.选项和填充

可选项和填充部分的⻓度为4n字节(n是整数),该部分是根据需要⽽增加的选项。如果不⾜4n字节,要加填充位,使得选项⻓度为32位(4字节)的整数倍,具体的做法是在这个字段中加⼊额外的零,以确保TCP头是32位(4字节)的整数倍。

最常⻅的选项字段是MSS(Maximum Segment Size最⻓报⽂⼤⼩),每个连接⽅通常都在通信的第⼀个报⽂段(SYN标志为1的那个段)中指明这个选项字段,表示当前连接⽅所能接受的最⼤报⽂段的⻓度。

由于可选项和填充部分不是必须的,所以TCP报⽂⾸部最⼩⻓度为20个字节。

⾄此, TCP报⽂⾸部的字段,就全部介绍完了。 TCP报⽂⾸部的后⾯,接着的是数据部分,不过数据部分是可选的。在⼀个连接建⽴和⼀个连接终⽌时,双⽅交换的报⽂段仅有TCP⾸部。如果⼀⽅没有数据要发送,也使⽤没有任何数据的⾸部来确认收到的数据,⽐如在处理超时的过程中,也会发送不
带任何数据的报⽂段

3. TCP的三次握手

通常情况下,建⽴连接的双⽅,由⼀端打开⼀个监听套接字(ServerSocket)来监听来⾃请求⽅的TCP(Socket)连接,当服务器端监听开始时,必须做好准备接受外来的连接,在Java中该操作通过创建⼀个ServerSocket服务监听套接字实例来完成,

下面我用java代码来实现服务端和客户端代码

服务端

package SCConnect;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Serive {
    public static void main(String[] args) throws IOException {
        //创建对象并绑定端口
        ServerSocket ss = new ServerSocket(8888);

        //等待客户端来连接
        Socket socket = ss.accept();

        //读取数据并保存到本地文件中

        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\文档\\day01-04\\touxiang.jpg"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }


        //回写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("上传成功!");
        bw.newLine();
        bw.flush();

        socket.close();
        ss.close();
        bos.close();
        bis.close();

    }
}


客户端

package SCConnect;

import java.io.*;
import java.net.Socket;

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        //读取本地文件中的数据,并写到服务器中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\java\\ideal\\testIO\\A.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        int len;
        byte[] bytes = new byte[1024];
        while ((len =bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.flush();
        //往服务器写出结束标记
        socket.shutdownOutput();

        //接受服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line =br.readLine();
        System.out.println(line);


        socket.close();




    }
}

这里我是向服务端上传了一个文件

其中这个ip地址要写自己要链接的地址,输出的文件地址也要写自己的文件地址,下面我用WireShark抓包来给大家逐步分析下

三次握手

在这里插入图片描述

(1)第⼀次握手:Client进⼊SYN_SENT状态,发送⼀个SYN帧来主动打开传输通道,该帧的SYN标志位被设置为1,同时会带上Client分配好的SN序列号,该SN是根据时间产生的⼀个随机值,通常情况下每间隔4ms会加1。除此之外,SYN帧还会带⼀个MSS(最大报文段长度)可选项的值,表示客户端发送出去的最⼤数据块的长度。
(2)第⼆次握手:Server端在收到SYN帧之后,会进⼊SYN_RCVD状态,同时返回SYN+ACK帧给Client,主要目的在于通知Client,Server端已经收到SYN消息,现在需要进行确认。Server端发出的SYN+ACK帧的ACK标志位被设置为1,其确认序号ANAcknowledgmentNumber)值被设置为Client的SN+1;SYN+ACK帧的SYN标志位被设置为1,SN值为Server端生成的SN序号;SYN+ACK帧的MSS(最大报文段长度)表示的是Server端的最大数据块长度。
(3)第三次握手:Client在收到Server的第⼆次握手SYN+ACK确认帧之后,首先将自己的状态会从SYN_SENT变成ESTABLISHED,表示自己方向的连接通道已经建立成功,Client可以发送数据给Server端了。然后,Client发ACK帧给Server端,该ACK帧的ACK标志位被设置为1,其确认序号AN(Acknowledgment Number)值被设置为Server端的SN序列号+1。还有⼀种情况,Client可能会将ACK帧和第⼀帧要发送的数据,合并到⼀起发送给Server端。
(4)Server端在收到Client的ACK帧之后,会从SYN_RCVD状态会进入ESTABLISHED状态,至此,Server方向的通道连接建立成功,Server可以发送数据给Client,TCP的全双工连接建立完成

在这里插入图片描述
Client和Server完成了三次握手后,双方就进⼊了数据传输的阶段。数据传输完成后, 连接将断开,连接断开的过程需要经历四次挥手。

四次挥手

在这里插入图片描述

业务数据通信完成之后,TCP连接开始断开(或者拆接)的过程,在这个过程中连接的每个端的都能独立地、主动的发起,断开的过程TCP协议使用了四路挥手操作。

四次挥⼿具体过程,具体如下:
(1)第⼀次挥手:主动断开方(可以是客户端,也可以是服务器端),向对方发送⼀个FIN结束请求报文,此报文的FIN位被设置为1,并且正确设置Sequence Number(序列号)和Acknowledgment Number(确认号)。发送完成后,主动断开方进⼊FIN_WAIT_1状态,这表示主动断开方没有业务数据要发送给对方,准备关闭SOCKET连接了。
(2)第⼆次挥手:正常情况下,在收到了主动断开方发送的FIN断开请求报文后,被动断开方会发送⼀个ACK响应报文,报文的Acknowledgment Number(确认号)值为断开请求报⽂的Sequence Number (序列号)加1,该ACK确认报文的含义是:“我同意你的连接断开请求”。之后,被动断开方就进⼊了CLOSE-WAIT(关闭等待)状态,TCP协议服务会通知⾼层的应⽤进程,对方向本地方向的连接已经关闭,对方已经没有数据要发送了,若本地还要发送数据给对方,对方依然会接受。被动断开方的CLOSE-WAIT(关闭等待)还要持续⼀段时间,也就是整个CLOSE-WAIT状态持续的时间。主动断开方在收到了ACK报文后,由FIN_WAIT_1转换成FIN_WAIT_2状态。
(3)第三次挥手:在发送完成ACK报文后,被动断开方还可以继续完成业务数据的发送,待剩余数据发送完成后,或者CLOSE-WAIT(关闭等待)截⽌后,被动断开方会向主动断开⽅发送⼀个FIN+ACK结束响应报文,表示被动断开方的数据都发送完了,然后,被动断开方进⼊LAST_ACK状态。
(4)第四次挥手:主动断开方收在到FIN+ACK断开响应报⽂后,还需要进行最后的确认,向被动断开方发送⼀个ACK确认报文,然后,自己就进⼊TIME_WAIT状态,等待超时后最终关闭连接。处于TIME_WAIT状态的主动断开方,在等待完成2MSL的时间后,如果期间没有收到其他报文,则证明对方已正常关闭,主动断开方的连接最终关闭。被动断开方在收到主动断开方的最后的ACK报文以后,最终关闭了连接,自己啥也不管了。
四次挥手的全部交互过程,具体如下图所示:

在这里插入图片描述

常见的面试题

1… 为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?

关闭连接时,被动断开方在收到对方的FIN结束请求报文时,很可能业务数据没有发送完成,并不能直接立即关闭连接,被动方只能先回复一个ACK响应报文,告诉主动断开方:“你发的FIN报文我收到了,只有等我所有的业务报文都发送完了,我才能真正的结束,在结束之前,我会发你FIN+ACK报文的,你先等着”。所以,被动断开方的确认报文,需要拆开成为两步,故总体就需要四次挥手。

而建立连接场景中, Server端的应答可以稍微简单一些。当Server端收到Client端的SYN连接请求报文后,其中ACK报文表示对请求报文的应答,SYN报文用来表示服务端的连接也已经同步开启了,而ACK报文和SYN报文之间,不会有其他报文需要发送,故可以合二为一,可以直接发送一个SYN+ACK报文。所以,在建立连接时,只需要三次握手即可。

2.为什么连接建立的时候是三次握手,可以改成两次握手吗?

三次握手完成两个重要的功能:一是双方都做好发送数据的准备工作,而且双方都知道对方已准备好。二是双方完成初始SN序列号的协商,双方的SN序列号在握手过程中被发送和确认。

如果把三次握手改成两次,可能会发生死锁。两次握手的话,缺失了Cilent的二次确认ACK帧,在这里插入图片描述

这是一个假想图,Client向Server发送一个SYN请求帧,Server收到后发送了确认应答的SYN+ACK帧,按照两次握手的协定,Server认为连接已经成功地建立了,可以开始发送数据帧。这个过程中,如果确认应答SYN+ACK帧传输中被丢失,Client没有收到,Client将不知道Server是否已经准备好,也不知道Server的SN序列号,Client认为连接还未建立成功,将忽略Server发来的任何数据,会一直等待Server的SYN+ACK确认应答帧。而Server在发出数据帧后,一直没有收到对应的数据帧后,一直没有收到对应的ACK确认后,就会产生超时,重复发送同样的数据帧,就形成了死锁

3.为什么主动断开方在TIME-WAIT状态必须等待2MSL的时间

在TCP协议中,当一端主动断开连接时,会进入 TIME-WAIT 状态,并等待一段时间(2倍的最大报文段生存时间,MSL)才能彻底关闭连接。这个等待时间主要有以下几个原因:

1.确保旧连接的所有数据都被接收:TIME-WAIT 状态的目的是为了确保网络中的所有数据报文都被接收和处理完毕。即使另一端已经发送了关闭连接的确认报文(FIN),但在网络中可能仍然有一些报文需要运行一段时间才能到达。等待一段时间可以确保这些延迟的报文到达并被接收。

2.防止旧连接的报文在新连接中混淆:如果一个新的连接在旧连接的 TIME-WAIT 状态结束前建立起来,并且使用了相同的 IP 地址和端口号,那么可能会导致旧连接的持续报文被错误地传递给新连接。通过等待一段时间,可以确保旧连接的所有报文都从网络中消失,避免混淆。

3.允许旧连接的重传报文到达目的地:在 TCP 协议中,报文的重传是常见的情况。在 TIME-WAIT 状态期间,旧连接的任何重传报文都可以被正确地处理。如果立即关闭连接而不等待,那么可能会导致重传报文被误认为是新连接的报文。

总结起来,等待 2MSL(最大报文段生存时间)的时间可以确保旧连接的所有数据都被接收和处理,防止旧连接的报文与新连接混淆,并允许旧连接的重传报文到达目的地。这样可以保证网络的可靠性和连接的正确关闭。

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

TCP还设有⼀个保活计时器,Client端如果出现故障,Server端不能⼀直等下去,这样会浪费系统资源。每收到⼀次Client客户端的数据帧后,Server端都的保活计时器会复位。计时器的超时时间通常是设置为2小时,若2小时还没有收到Client端的任何数据帧,Server端就会发送⼀个探测报文段,以后每隔75秒钟发送⼀次。若⼀连发送10个探测报文仍然没反应,Server端就认为Client端出了故障,接着就关闭连接。如果觉得保活计时器的两个多⼩时的间隔太长,可自行调整TCP连接的保活参数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值