【TCP】老生常谈的TCP的三次握手与四次挥手

前提提要

TCP三次握手与四次挥手这都已经是老生常谈的话题了,基本上每个程序员都能对这个流程来上几句。但是大部分人只能说出一个大致的流程,少了一个比较完整的总结。于是乎有了这篇文章的出现。这篇文章是我自己学习的总结,如有不足,希望各路大佬能指出。

TCP首部格式与重要字段解析
在这里插入图片描述
想要知道这个流程,需从根源看起,了解TCP首部的一些字段,才不至于在看流程的时候对一些关键字眼云里雾里。

  • 序号(seq):大小为4个字节,其作用是标记数据字节的顺序。TCP把连接中所有发送的数据字节都标上序号。第1个字段的序号由本地随机产生,接下的字段就是在这个序号上去增加。
  • 确认号(ack):注意这里是小写,区别于下面的标志位ACK,大小是4个字节,表示的是期待收到对方下一个报文段第一个数据字节的序号。序号表示报文段携带数据第一个字节的编号,而确认号指的是期望收到下一个字节的编号,所以这个ack就是当前报文段最后一个字节的编号+1。
  • 标志位(ACK):大小占1位,当ACK为1的时候,上面的确认号ack才能生效
  • 同步(SYN):建立连接的时候所用到,只有在TCP建立连接的时候才会被置为1,握手完成后SYN又会被置为0。SYN=1 ACK=0,表示这是一个连接请求报文段。
  • 终止(FIN):当FIN=1时,表示此报文段的发送方数据已经发送完成,要求释放连接。
  • URG:URG为1时候表示紧急处理。
  • PSH:该位为1的时候,表示需要将收到的数据立刻上传给上层应用协议,若为0,则会将数据先进行缓存。
  • RST:表示重新建立连接。

三次握手的流程
在这里插入图片描述
三次握手的过程详解

  • 第一次握手:建立连接时,客户端发送SYN包给服务器,其中包含客户端的初始化序号seq=x,并进入SYN-SENT状态。然后等待服务器确认,这个SYN包里面SYN=1,ACK=0,表示这是个TCP连接请求包。
  • 第二次握手:服务器收到客户端请求后,必须确认客户端的数据包。同时自己也会发送一个SYN包,即SYN+ACK包,然后进入SYN_RECV状态。服务端的确认报文中,标识位SYN=1,ACK=1,表明这是个TCP连接响应报文,并包含服务端的初始化序号seq=y,以及服务器对客户端的初始化序号的确认号(客户端的seq+1,即ack = x+1)。
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕之后,客户端和服务器进入ESTABLISHED状态,即连接成功,完成三次握手。

在这里插入图片描述

四次挥手的流程
在这里插入图片描述
四次挥手过程详解

  • 1、客户端发送连接释放报文,并停止发送数据。释放报文首部,FIN=1,其序列号为seq=u(这里的u为前面已经传送过来的数据最后一个字节+1),然后客户端进入了FIN-WAIT-1(终止状态1)。TCP要求,就算释放报文不携带数据,也要消耗一个序号。
  • 2、服务端接收到连接释放报文。发送确认报文,ACK=1,ack=u+1,自己的序列号seq=v,然后自己就进入关闭等待状态,这个时候客户端就不会向服务端发送数据了,但是服务端可能还有一些数据没有发送完,所以需要等待服务端发送完数据。
  • 3、客户端收到服务端的确认包后,客户端就会进入FIN-WAIT-2状态,等待服务端发送连接释放报文FIN,在这之前还能够接受服务器发送的最后数据。
  • 4、服务器发送完数据后,就会发送连接释放报文,FIN=1,ack=u+1。在半关闭状态中,服务器可能又发送了一些数据,假设目前服务器发送的序号seq=w,然后服务器就进入了LAST-ACK(最后确认状态)。
  • 5、客户端收到服务器端的连接释放报文后会再发出一次确认,ACK=1,ack=w+1,seq=u+1,然后客户端进入TIME-WAIT(时间等待状态)。这个时候TCP连接还没释放,必须等待2MSL时间后,当客户端撤销相应的TCB后,才进行CLOSED状态。
  • 6、服务器只要收到客户端发出的确认,立即就进入CLOSED状态。服务端进入CLOSED的状态比客户端要早一点。

解疑

1、为什么客户端要等待2MSL(最大报文生存时间)?
客户端确认自己发送的确认报文能够到达服务器端,如果服务端没有收到客户端的确认报文,就会不断地发出连接释放报文,所以客户端等待2MSL就是为了处理这种情况。

2、为什么连接的时候是三次握手,关闭的时候是四次握手?
建立连接的时候,当服务端收到客户端的SYN连接请求后,可以直接发送SYN+ACK报文。ACK是用来确认的,SYN是用来同步的。但是关闭连接时候,服务端收到FIN报文的时候,并不会马上关闭SOCKET,只会先回复一个ACK确认报文,但是还要等待服务端所有数据都发送完成,才会发送FIN报文,所以这个ACK报文和FIN报文是分两次发送的,因此需要四次。

3、为什么要用三次握手进行连接?
三次握手能够让客户端和服务端清楚自己的发送接受能力都是能够正常使用的。举个例子,只进行两次握手。首先客户端向服务端发送连接请求,服务端收到连接请求后,发送应答分组, 这个时候服务端就认为连接已经成功建立,于是开始发送数据分组。但是如果客户端没有收到这个应答分组,他就认为连接还没建立成功,就会把服务端发来的全部数据分组给忽略,只等待应答分组。然后服务端在数据分组超时后之后,又会不断向客户端发送数据,就造成了死锁的现象

4、数据发送过程中,客户端出现故障怎么处理?
TCP有保活计时器,服务器在收到客户端的应答的时候,就会重置这个计时器,一般所用的时间设置为2小时,两小时内没收到客户端的数据,服务端就会发送探测报文段,每隔75秒发一次,连发10次没放映,服务器就会关闭连接。

5、TCP长连接和短连接的区别?
短连接:客户端向服务端发送消息,服务端回应客户端,在这一次读写之后,双方都可以发起一个close操作。说白了短连接就是一次读写操作
长连接:客户端和服务端完成一次读写之后,他们之间的连接并不会关闭,后续的读写还是会复用这个连接

6、TCP的粘包和拆包问题,以及其解决方法

为什么只说TCP的问题?
因为UDP是基于报文发送的,其UDP首部用16bit指示UDP数据报文的长度,所以应用层能够很好地将不同的数据报文区分,从而避免这些问题。而TCP是基于字节流,应用层和TCP传输层之间传输的数据是大小不等的数据块,TCP并没有把这些数据块区分出边界。而且TCP首部也没有表示数据长度的字段。基于上述两个问题,所以TCP才会出现粘包或者拆包现象。

粘包和拆包的发生条件?

  1. 要发送的数据大于TCP发送缓存区剩余空间大小,将会发生拆包
  2. 要发送的数据大于最大报文长度,TCP将会在传输前进行拆包
  3. 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次性发出,就会出现粘包
  4. 接收数据的应用层没有及时读取缓冲区中的数据就会出现粘包

解决方法
TCP是面向字节流的,本身无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组,只能通过上层应用协议来解决。

  1. 消息定长:发送端将每个数据包都封装为固定长度(不够的可能通过0补充),这样每次从缓冲区读取固定长度的数据就自然而然地把每个数据包拆分开
  2. 设置消息边界:服务端从网络流中按照消息边界分离出消息内容,在包尾增加回车换行符进行分割,比如FTP将
  3. 消息分为消息头和消息体:消息头包含表示消息总长度字段

TCP如何实现可靠传输?
TCP使用超时重传来实现可靠传输,如果一个已经发送的报文段在超时时间内没有收到回复,就会重传这个报文段。

TCP的滑动窗口
窗口是缓存的一部分,用来暂存字节流,接收方和发送方各有一个窗口,接收方通过TCP报文段中的窗口字段告诉发送方自己的窗口大小,发送方会根据这个值和其他信息来设置自己的窗口大小。发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右移动一定距离,直到左部第一个字节不是已发送并且确认的状态。接收窗口只会对窗口内最后一个按序到达的字节进行确认,

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值