TCP 在传输之前会进行三次沟通,一般称为三次握手
;传完数据断开的时候要进行四次沟通,一般称为四次挥手
。
什是 TCP 协议
TCP
是 Transmission Control Protocol 的简称,中文名是传输控制协议
,是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。在简化的计算机网络 OSI 模型中,完成第四层传输层所指定的功能。
TCP
是面向连接的、可靠的、缓慢的、可靠交付以及保证消息顺序的。TCP
用于实现可靠传输的情况,文件非常重要,对网络拥堵有较高的要求的情况。
TCP 报文格式
TCP
报文是 TCP 层传输的数据单元,也叫报文段,分为报头
和数据
两部分。TCP
报文的格式如下图所示:
报头
包含以下信息:
源端口(16位):
源端口用来标识报文的返回地址。目的端口(16位):
目的端口指明接收方计算机上的应用程序接口。序列号(32位):
也称为顺序号(Sequence Number),简写为 seq。TCP 是面向字节流的,TCP 连接中传送的字节流中的每个字节都按顺序编号。整个要传送的字节流的起始序号必须要在连接建立时设置。确认号(32位):
也称为应答号(Acknowledgment Number),简写为 ack。在握手阶段,确认序号将发送方的序号加1作为回答。数据偏移(4位):
也成为首部长度,TCP 的头长度最长可为60字节(二进制1111换算为十进制为15,15*4字节=60字节)。保留(6位):
为将来定义新的用途保留,现在一般置0。标志位(6位):
TCP 报头信息中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控 TCP 的状态机的,依次为URG
、ACK
、PSH
、RST
、SYN
、FIN
。窗口(16位):
滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小是一个16bit字段,因而窗口大小最大为65535。检验和(16位):
检验和覆盖了整个的 TCP 报文段: TCP 首部和 TCP 数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。紧急指针(16位):
一般不使用,只有当 URG 标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。选项(长度可变):
长度可变,最长可达40字节。通常为空,可根据首部长度推算。用于发送方与接收方协商最大报文段长度(Maximum Segment Size,简称 MSS)或在高速网络环境下作窗口调节因子时使用。首部字段还定义了一个时间戳选项。
序号
TCP
是一个面向连接的、可靠的传输协议。而这个可靠传输的功能则是靠32位序列号
和32位确认号
实现。
序列号(Sequence Number):
seq 序号,占32位,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记。在 TCP 传送的流中,每一个字节一个序号,序号是本报文段发送的数据组的第一个字节的序号,所以序号确保了 TCP 传输的有序性。确认号(Acknowledgment Number):
ack 序号,占32位,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当 ACK 标志为1时才有效,ack=seq+1。
序列号和确认号是 TCP 可靠传输的关键部分。
标志位
TCP 报头信息中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控 TCP 的状态机的,依次为 URG
、ACK
、PSH
、RST
、SYN
、FIN
。每个标志位的含义如下:
标志位 | 含义 |
---|---|
URG | 表示 TCP 包的紧急指针域有效,用来保证 TCP 连接不被中断,并且督促中间层设备要尽快处理这些数据。 |
ACK | 表示应答域有效,就是说前面所说的 TCP 应答号将会包含在 TCP 数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。 |
PSH | 表示 Push 操作。所谓 Push 操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队。 |
RST | 表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包。 |
SYN | 表示同步序号,用来建立连接。SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1(这个标志的数据包经常被用来进行端口扫描)。 |
FIN | 表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送 FIN 标志位的 TCP 数据包后,连接将被断开(这个标志的数据包也经常被用于进行端口扫描)。 |
需要注意的是:不要将
确认号 ack
与标志位 ACK
搞混了;确认方ack=发起方req+1
,两端配对。
TCP 连接的建立(三次握手)
TCP
是面向连接的传输层协议
,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。当主动方发出 SYN
连接请求后,等待对方回答 SYN+ACK
,并最终对对方的 SYN
执行 ACK
确认。
TCP 三次握手的过程如下:
- 第一次握手:建立连接时,客户端发送
SYN
报文段,设置seq=x
;然后,客户端进入SYN_SEND
状态,等待服务器的确认; - 第二次握手:服务器收到客户端的
SYN
报文段,需要确认客户端的SYN
报文段,设置ack=x+1(seq+1)
,同时自己也发送一个SYN
报文段,设置seq=y
(即SYN+ACK
包),此时服务器进入SYN_RECV
状态; - 第三次握手:客户端收到服务器的
SYN+ACK
报文段。然后设置ack=y+1(seq+1)
,向服务器发送ACK
报文段,发送完成后,客户端和服务器端都进入ESTABLISHED
状态,完成 TCP 三次握手。
完成三次握手后,客户端和服务器端就可以开始传送数据。
在
TCP/IP
协议中,TCP
协议提供可靠的连接服务,连接是通过三次握手
进行初始化的。三次握手
的目的是同步连接双方的序列号和确认号并交换TCP
窗口大小信息。
TCP 连接的释放(四次挥手)
TCP
连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个 FIN
来终止这一方向的连接,收到一个 FIN
只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个 TCP
连接上仍然能够发送数据,直到这一方向也发送了 FIN
。
TCP 四次挥手的过程如下:
- 第一次挥手:主机A(可以是客户端,也可以是服务端)发送一个
FIN
报文段,设置seq=x
,用来关闭主机A到主机B的数据传送,此时,主机A进入FIN_WAIT_1
状态; - 第二次挥手:主机B收到主机A发送的
FIN
报文段,向主机A发回一个ACK
,设置seq=x+1
,主机A进入FIN_WAIT_2
状态; - 第三次挥手:主机B向主机A发送
FIN
报文段,设置seq=y
,请求关闭连接,同时主机B进入CLOSE_WAIT
状态; - 第四次挥手:主机A收到主机B发送的
FIN
报文段,设置seq=y+1
,向主机B发送ACK
报文段,然后主机A进入TIME_WAIT
状态;主机B收到主机A的ACK
报文段以后,就关闭连接。
完成四次挥手后,客户端和服务器端建立的连接被释放。
无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0 却由服务器执行主动关闭。
总结
TCP
连接从建立到释放的过程如下图所示:
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
建立连接的时候, 服务器在LISTEN
状态下,收到建立连接请求的SYN
报文后,把ACK
和SYN
放在一个报文里发送给客户端。而关闭连接时,服务器收到对方的FIN
报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN
报文给对方来表示同意现在关闭连接,因此,己方ACK
和FIN
一般都会分开发送,从而导致多了一次。如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP
还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。