TCP三次握手

TCP协议是一个我们每天都在使用的网络通讯协议,因为绝大部分的网络连接都是建立在TCP协议上的,比如你此刻正在看的这篇文章是建立在HTTP应用层协议的基础上的,而HTTP协议的底层则是建立在TCP的传输层协议上的。

下面我们来分析一下TCP三次握手的执行流程,以及为什么需要三次握手?

TCP是什么

TCP(Transmission Control Protocol,传输控制协议)是一个面向连接的、可靠的、基于字节流的传输层协议。从它的概念中我们可以看出TCP的三个特点:面向连接、可靠性和面向字节流。

TCP的三大特点:

  • 面向连接:是指客户端与服务端进行通信前需要先建立连接。那么连接又是什么呢?用于保证可靠性和流控制机制的信息,包括Socket、序列号及窗口大小被称为连接。其中,Socket是由IP地址加端口号组成的,序列号是用来解决乱序问题的,而窗口大小则是用来做流量控制的。
  • 可靠性:是指无论网络环境多差,TCP都可以保证信息一定能够准确的传递给接收端。怎么保证可靠性呢?一是状态性,TCP会记录信息的发送状态,哪些数据收到了,哪些信息没收到。二是可控制性,TCP会根据状态情况控制自己的行为,例如当TCP认为丢包了就会通知客户端重传。
  • 面向字节流:以字节流的方式进行数据传输。

TCP的报文格式

Wireshark抓取TCP三次握手包_三次握手

Wireshark抓取TCP三次握手包_tcp_02

根据上面TCP的报文格式和使用wireshark抓取第一次握手的报文进行分析:

源端口号(2个字节):34 16(13334)

目的端口号(2个字节):22 c3(8899)

序号(4个字节):16 39 68 74,用来标识发送端向接收端发送的数据字节流。

确认序号(4个字节):00 00 00 00,由于该报文为SYN报文,ACK标志为0,故没有确认序号(ACK标志为1时确认序号才有效),一旦连接建立,该值将始终发送(同ACK标志)。

首部长度(4位):8(首位长度只有4位,故只能取16进制的80的前四位8),首部长度的单位为32位字,故该报文长度为8*32/8=32个字节。存在该字段是因为TCP报头中选项字段长度可变,报头不包含任何选项字段则长度为20字节;4位所能表示的最大值为1111,转化为10进制为15,15*32/8=60,故报头最大长度为60字节。

保留(6位):000000

标志位(6位):000010,表示同步序号标志SYN为1。

标志位

说明

URG(Urgent)

紧急指针有效性标志

ACK(Acknowledge Character)

确认序号有效性标志,一旦一个连接建立起来,该标志总被置为1,即除了请求建立连接报文(仅设置Syn标志位为1),其它所有报文的该标志总为1

PSH(Push)

Push标志(接收方应尽快将报文段提交至应用层)

RST(Reset)

重置连接标志

SYN(Synchronize Sequence Numbers)

同步序号标志

FIN(Finish)

传输数据结束标志

窗口大小(2字节):20 00(8192),由于2字节能够表示的最大正整数为65535,故窗口最大值为65535。

检验和(2字节):00 28,用来检查TCP头在传输中是否被修改。

紧急指针(2字节): 00 00,当Urgent标志置1时,紧急指针才有效。

TCP三次握手的执行过程

Wireshark抓取TCP三次握手包_wireshark_03

TCP 的执行流程如下:

  1. 最开始时客户端和服务端都处于CLOSED状态,然后服务端先主动监听某个端口,此时服务器端就变成了 LISTEN(监听)状态;
  2. 然后客户端主动发起连接,发送SYN(同步序列编号),此时客户端就变成了SYN-SENT状态;
  3. 服务端接收到信息之后返回SYN和ACK至客户端,此时服务器端就变成了SYN-REVD状态;
  4. 客户端接收到消息之后,再发送ACK至服务器端,此时客户端就变成了ESTABLISHED(已确认)状态,服务端收到ACK之后,也变成了ESTABLISHED状态,此时连接工作就执行完了。

为什么要三次握手

了解了以上TCP的基础概念之后,我们再来看一下TCP为什么需要三次握手?

防止重复连接

三次握手的主要原因是为了防止旧的重复连接引起连接混乱问题。

比如在网络状况比较复杂或者网络状况比较差的情况下,发送方可能会连续发送多次建立连接的请求。如果TCP握手的次数只有两次,那么接收方只能选择接受请求或者拒绝接受请求,但它并不清楚这次的请求是正常的请求,还是由于网络环境问题而导致的过期请求,如果是过期请求的话就会造成错误的连接。

所以如果TCP是三次握手的话,那么客户端在接收到服务器端SEQ+1的消息之后,就可以判断当前的连接是否为历史连接,如果判断为历史连接的话就会发送终止报文(RST)给服务器端终止连接;如果判断当前连接不是历史连接的话就会发送指令给服务器端来建立连接。

同步初始化序列

TCP为了保证在不稳定的网络环境中构建一个稳定的数据连接,它就需要一个“序列号”字段来保证自己的稳定性,而这个序列号的作用就是防止数据包重复发送,以及有效的解决数据包接收时顺序颠倒的问题。

那么在建立TCP连接时就需要同步初始化一个序列号来保证TCP的稳定性,因此它需要执行以下过程:

  1. 首先客户端发送一个携带客户端初始序列号的SYN报文给服务器端;
  2. 服务端接收到消息之后会回复一个ACK的应答报文,表示客户端的SYN报文已被服务端成功接收了;然后发送一个携带服务器端初始序列号的SYN报文给客户端(这里本来应该分为两个连接,为了节约网络资源合并到一起)。
  3. 客户端收到消息之后也会发送一个ACK给服务端,服务器端拿到这个消息之后,我们就可以得到双方可靠的初始化序列号了。

而如果是两次握手的话,只能进行一方的序列号的确认,所以TCP连接至少需要三次握手。

当然TCP连接还可以四次握手,甚至是五次握手,也能实现TCP连接的稳定性,但三次握手是最节省资源的连接方式。

下面附上使用wireshark抓取的三次握手的报文图:

第一次握手上面有。

第二次握手:

Wireshark抓取TCP三次握手包_抓包_04

第三次握手:

Wireshark抓取TCP三次握手包_tcp_05


更多精彩内容关注本人公众号:架构师升级之路

Wireshark抓取TCP三次握手包_tcp_06