概念
传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP 协议的特点
面向连接
应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接。
根据
RFC 793
的定义:端口号拼接到(concatenated with) IP 地址即构成了套接字socket = (IP:PORT)
。每一条 TCP 连接唯一地被通信两端的两个端点(即两个套接字)所确定连接必须一对一
每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的
面向字节流
虽然应用程序和 TCP 的交互是一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的无结构的字节流。TCP 并不知道所传送的字节流的含义
TCP 有一个缓冲,当应用程序传送的数据块太长,TCP 就可以把它划分短一些再传送。如果应用程序传送的数据太短,TCP 也可以等待积累有足够多的字节后再构成报文段发送出去。
提供可靠交付的服务
通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达。
实际的网络环境中传输信道可能会产生差错,丢失数据,数据被阻塞。当出现差错或丢失时让发送方重传,过滤多次重传造成的重复数据。
拥塞控制
网络出现的拥塞会使源主机的发送速率降低
提供全双工通信
全双工(Full Duplex)是指数据可以在一个信号载体的两个方向上传输,既可以发送数据,也可以接收数据,且两者可以同步进行。
半双工(Half Duplex) 是指数据可以在一个信号载体的两个方向上传输,既可以发送数据,也可以接收数据,但两者不能同步进行。
单工是指数据只能在信号载体的一个方向上传输,要么只能发送数据,要么只能接收数据。
TCP 报文格式
源端口和目的端口
用来标识同一台计算机的不同的应用进程
TCP 报头中的源端口号和目的端口号同 IP 数据报中的源 IP 与目的 IP 唯一确定一条 TCP 连接。
源端口和目的端口各占
2 Byte = 16 bit
。序号和确认序号
序号和确认号是 TCP 可靠传输的关键部分。
序号是本报文段发送的数据的第一个字节的序号。在 TCP 传送的流中,每一个字节一个序号。例如:一个报文段的序号为 300,此报文段数据部分共有 100 字节(序号
300~399
),则下一个报文段的序号为 400。序号确保了 TCP 传输的有序性。确认号指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当 ACK 标志为 1 时才有效。比如建立连接过程中的第一次握手,不需要确认号,SYN 报文的 ACK 标志位为 0。
序号和确认序号各占
4 Byte = 32 bit
数据偏移/首部长度
数据偏移:TCP 报文段的数据起始处离 TCP 报文段的起始处有多远。这个距离其实就是 TCP 首部的长度。
由于首部可能含有长度不确定的选项字段,因此 TCP 首部的长度是不确定的,首部如果不包含任何任选字段则长度为 20 字节
数据偏移占
4 bit
(4 位二进制数能表示的最大十进制数为 15),数据偏移的单位是 32 位字(4字节),所以首部长度的最大值是15*4 = 60
字节,即选项最长是60 - 20 = 40
字节(10个选项)。保留
为将来定义新的用途保留,现在一般置0。
控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。
SYN
用于建立连接过程同步序号。在连接请求中,
SYN=1
和ACK=0
表示该数据段只有序号,没有捎带确认;在连接应答中,SYN=1
和ACK=1
表示除了同步序号,还有捎带确认。注:捎带是指对客户机到服务器数据的确认被装载在一个承载服务器到客户机的数据报文段中。
ACK
当
ACK=0
时,表示该数据段不包含确认信息,当ACK=1
时,表示该报文段包括一个对已被成功接收报文段的确认。FIN
finish 标志,用于释放一个连接,表示发送方已经没有数据要传输了
URG
指示报文段里存在着被发送方的上层实体标记为”紧急”数据,当URG=1时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序号的字节偏移量),TCP接收方必须通知上层实体。
PSH
当PSH=1时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。
RST
用于重置一个已经混乱的连接(如主崩溃),也可用于拒绝一个无效的数据段或者拒绝一个连接请求。一般而言,如果你得到的数据段被设置了RST位,那说明你这一端有问题了。
窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小是一个16bit字段,因而窗口大小最大为65535。
校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。
紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。
选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。
数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
TCP 三次握手建立连接
TCP 作为一种可靠传输控制协议,其核心思想要保证数据的可靠传输。TCP 依据 seq
序号来做可靠重传或接收。TCP 依靠三次握手来确定双方的 ISN(初始 seq 序号)。
A 的 TCP 向 B 发出连接请求报文段:其首部中的同步位
SYN = 1
,并选择初始序号seq = x
,表明 A 在传送数据时的第一个数据字节的序号是 x。A 进入
SYN-SENT(同步已发送)
状态B 的 TCP 接收到连接请求报文段后,如同意,则发回确认:确认报文段的
SYN = 1
和ACK = 1
,确认号ack = x + 1
,同时也为自己选择一个初始序号seq = y
,表明 B 在传送数据时的第一个数据字节的序号是 y。B 进入
SYN-RCVD(同步收到)
状态A 收到 B 的确认报文段后,向 B 发送确认报文段:确认报文段的
ACK = 1
,序号seq = x + 1
,确认号ack = y + 1
。A 进入
ESTABLISHED(已建立连接)
状态。当 B 收到 A 的确认后,也进入ESTABLISHED
状态此 ACK 报文段可以携带数据,如果携带数据,则消耗序号,在这种情况下,下一个数据报文段的序号不再是
seq = x + 1
。不携带数据则不消耗序号
如果发生失败:
第一个包,即 A 发给 B 的 SYN 中途被丢,没有到达 B
在收到 B 的确认前,A 会周期性超时重传
第二个包,即 B 发给 A 的 SYN + ACK 中途被丢,没有到达 A
收到 A 的确认前,B 会周期性超时重传
第三个包,即 A 发给 B 的 ACK 中途被丢,没有到达 B,A 发完 ACK,单方面认为 TCP 为 Established 状态:
a. 假定此时双方都没有数据发送,在收到 A 的确认前,B 会周期性超时重传,收到 A 的确认之后 B 的 TCP 连接也为 Established 状态,双向可以发包。
b. 假定此时 A 有数据发送,B 收到 A 的 Data + ACK,也会切换为 established 状态,并接受 A 的 Data。
c. 假定 B 有数据发送,数据发送不了,会一直周期性超时重传 SYN + ACK,直到收到 A 的确认才可以发送数据。
TCP 四次挥手释放连接
A 的应用进程向其 TCP 发出连接释放报文段,并停止再发送数据,等待 B 的确认:
FIN = 1
,序号seq = u
(经过数据传输,A 的序号已经发生变化,假定下一个序号为 u)A 进入
FIN-WAIT-1(终止等待1)
状态B 接收到 A 的连接释放报文段后,发出确认报文段:
ACK = 1
,确认号ack = u + 1
,序号seq = v
(经过数据传输,B 的序号已经发生变化,假定下一个序号为 v)B 进入
CLOSE-WAIT(关闭等待)
状态A 收到确认报文后,A 到 B 这个方向的连接就释放了,TCP 连接出于
半关闭
状态。B 若发送数据,A 仍要接收。A 进入
FIN-WAIT-2(终止等待2)
状态如果 B 也没有要向 A 发送的数据,其应用进程向其 TCP 发出连接释放报文段,并停止再发送数据,等待 A 的确认:连接释放报文段的
FIN = 1
,ACK = 1
,确认号ack = u + 1
, 序号seq = w
(经过数据传输,B 的序号可能又发生变化,假定下一个序号为 w)B 进入
LAST-ACK(最后确认)
状态A 接收到 B 的连接释放报文段后,发出确认报文段:
ACK = 1
,确认号ack = w + 1
,序号seq = u + 1
(连接释放报文消耗序号)A 进入到
TIME-WAIT(时间等待)
状态,必须等待2MSL
的时间,A 才进入到CLOSED
状态B 收到确认报文后,进入
CLOSED
状态
时间 MSL 叫做最长报文段寿命(Maximum Segment Lifetime),RFC 793建议设为2分钟。但这完全是从工程上来考虑的,对于现在的网络,MSL = 2分钟可能太长了一些
TIME-WAIT 等待时间
为什么 A 在 TIME-WAIT 状态必须等待 2MSL 的时间呢?
第一、为了保证 A 发送的最后一个 ACK 报文段能够到达 B。
这个 ACK 报文段有可能丢失,因而使处在 LAST-ACK 状态的 B 收不到对已发送的 FIN + ACK 报文段的确认。B 会超时 重传这个 FIN + ACK 报文段,而 A 就能在 2MSL 时间内收到这个重传的 FIN + ACK 报文段。接着 A 重传一次确认,重新启动 2MSL 计时器。最后,A 和 B 都正常进入到 CLOSED 状态。如果 A 在 TIME-WAIT 状态不等待一段时间,而是在发完 ACK 报文段后立即释放连接,那么就无法收到 B 重传的 FIN + ACK 报文段,因而也不会再发送一次确认报文段。这样,B 就无法按照正常步骤进入 CLOSED 状态
第二、防止“已失效的连接请求报文段”出现在新的连接中。
A 在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段