TCP 在IP不可靠的服务之上创建了可靠数据传输服务。TCP 的可靠数据传输服务确保一个进程从其接收缓存中读出的数据流是无损坏、无间隔、非冗余和按序的数据流;即该字节流与连接的另一端发出的字节流完全相同 。
TCP 被称为是面向连接的,在一个应用进程开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立确保数据传输的参数。 作为 TCP 连接建立的一部分,连接的双方都将初始化与 TCP 连接相关的 TCP 状态变量。
TCP 连接提供全双工服务:如果一台主机上的进程 A 与另一台主机上的进程 B 存在一条 TCP 连接,那么应用层数据就可在从进程 B 流向进程 A 的同时,也从进程 A 流向进程 B 。 TCP 连接也是点对点的,即在单个发送方与单个接收方之间的连接。 “多播”即在一次发送操作中,从一个发送方将数据传送给多个接收方,对 TCP 来说是不可能的 。
TCP通过三次握手建立连接。一旦建立起一条 TCP 连接,两个应用进程之间就可以相互发送数据了。客户进程通过套接字传递数据流。 TCP 将这些数据引导到该连接的发送缓存里,发送缓存是在三次握手时设置的缓存之一 。 接下来 TCP 就会不时从发送缓存里取出一块数据。TCP可从缓存中取出并放入报文段中的数据数量受限于最大报文段长度( Maximum Segmenl Size, MSS ) 。 MSS 通常根据最初确定的由本地发送主机发送的最大链路层帧长度(即所谓的最大传输单元( Maximum Transmission Unit, MTU)来设置 。
建立一个 TCP 连接需要客户端与服务器端同步三个信息:
- Socket:由 IP 地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
- 源地址,源端口,目的地址,目的端口
源地址和目的地址的字段(32位)在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。源端口和目的端口的字段(16位)在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
一、TCP报文段结构
1. CWR---拥塞窗口减小(发送方降低它的发送速率);2. ECE---其值为 1 则通知对方,从对方到这边的网络有阻塞。在收到数据包的 IP 首部中 ECN 为 1 时将 TCP 首部中的 ECE 设为 1 ;3. URG---该位设为 1,表示包中有需要紧急处理的数据 ;4. ACK---该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的SYN 包之外该位必须设置为 1 ;5. PSH---该位设为1表示需要将收到的数据立刻传给上层应用协议,若设为 0则先将数据进行缓存 ;6. RST---该位设为 1,表示 TCP 连接出现异常必须强制断开连接 ;7. SYN---用于初始化一个连接的同步序列号;8. FIN---该报文段的发送方已经结束向对方发送数据。
二、三次握手与四次挥手
三次握手
第一步:客户端的 TCP 首先向服务器端的 TCP 发送一个特殊的 TCP 报文段 。 该报文段中不包含应用层数据 。 在报文段的首部中的一个标志位(SYN 比特)被置为1。这个特殊报文段被称为 SYN 报文段。客户会随机地选择一个初始序号(client_isn ),并将此编号放置于该起始的 TCP SYN报文段的序号字段中 。 该报文段会被封装在一个 IP 数据报中发送给服务器 。
第二步:包含 TCP SYN 报文段的 IP 数据报到达服务器主机,服务器会从该数据报中提取出 TCP SYN 报文段,为该 TCP 连接分配 TCP 缓存和变量,并向该客户 TCP 发送允许连接的报文段。 (在完成三次握手的第三步之前分配这些缓存和变量,使得 TCP 易于受到称为 SYN 洪泛的拒绝服务攻击。 )这个允许连接的报文段也不包含应用层数据 。 但是,在报文段的首部却包含 3 个重要的信息 。 首先, SYN 比特被置为1。其次,该 TCP 报文段首部的确认号字段被置为 client_isn + 1 。 最后,服务器选择自己的初始序号( server_isn ),并将其放置到 TCP 报文段首部的序号字段中 。 该允许连接的报文段有时被称为 SYNACK 报文段( SYN + ACK segment ) 。
第三步:在收到 SYNACK 报文段后,客户端也要给该连接分配缓存和变量,同时向服务器发送另外一个报文段:这最后一个报文段对服务器的允许连接的报文段进行了确认(该客户通过将值 server_isn + 1 放置到 TCP 报文段首部的确认字段中来完成此项工作)。因为连接已经建立了,所以该 SYN比特被置为 0 。此阶段可以在报文段负载中携带客户到服务器的数据。
一旦完成这 3 个步骤,客户和服务器主机就可以相互发送包括数据的报文段了 。 在以后每一个报文段中, SYN 比特都将被置为 0。 为了创建该连接,在两台主机之间发送了 3 个分组。 由于这个原因 , 这种连接创建过程通常被称为 3 次握手。
四次挥手
客户端应用进程发出一个关闭连接命令。 这会引起客户 TCP 向服务器进程发送一个特殊的TCP 报文段 。 这个特殊的报文段让其首部中的 FIN 比特被设置为 1 。 当服务器接收到该报文段后,就向发送方回复一个确认报文段。 然后服务器发送它自己的终止报文段,其 FIN 比特被置为1。最后客户端对服务器的终止报文段进行确认,等待2MSL( Maximum Segment Life)后,用于该连接的所有资源都被释放了。
MSL
是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。TIME_WAIT至少需要持续2MSL时长,第一个MSL是为了等自己发出去的最后一个ACK从网络中消失,第二个是为了等在对端收到ACK之前可能重传的FIN报文从网络中消失。
状态转移图
特殊情况
当一台主机接收到一个 TCP 报文段,其端口号或源 IP 地址与该主机上进行中的套接字都不匹配时,例如一台主机接收了具有目的端口 80的一个 TCP SYN 分组,但该主机在端口 80 不接受连接(即它不在端口 80 上运行 Web 服务器)。则该主机将向源发送一个特殊重置报文段,该 TCP 报文段将 RST 标志位置为 1 。 当主机发送一个重置报文段时,它告诉该源“我没有那个报文段的套接字,不要再发送该报文段了” 。 当一台主机接收一个 UDP 分组,它的目的端口与进行中的 UDP 套接字不匹配,该主机发送一个特殊的 ICMP 数据报。
三、超时与重传
TCP 采用累积确认,发送方收到ACK的值 y 确认了字节编号在 y 前的所有字节都已经收到。TCP 重传具有最小序号的还未被确认的报文段。每次 TCP 重传时都会将下一次的超时间隔设为先前值的两倍。
超时触发重传存在的问题之一是超时周期可能相对较长 。 当一个报文段丢失时,这种长超时周期迫使发送方延迟重传丢失的分组,因而增加了端到端时延。发送方通常可在超时事件发生之前通过注意冗余 ACK 来较好地检测到丢包情况 。如果 TCP 发送方接收到对相同数据的 3 个冗余 ACK ,说明跟在这个已被确认过 3 次的报文段之后的报文段已经丢失 , TCP 就执行快速重传( fast retransmit ) ,即在该报文段的定时器过期之前重传丢失的报文段 。
TCP 确认是累积的,正确接收但失序的报文段不会被接收方逐个确认 。TCP 发送方仅需维持已发送过但未被确认的字节的最小序号( SendBase)和下一个要发送的字节的序号。选择确认:允许 TCP 接收方有选择地确认失序报文段,而不是累积地确认最后一个正确接收的有序报文段 。 当将该机制与选择重传机制结合起来使用时(即跳过重传那些已被接收方选择性地确认过的报文段) , TCP 看起来就像通常的 SR 协议 。
四、流量控制
TCP 提供流量控制服务以消除发送方使接收方缓存溢出的可能。流量控制因此是一个速度匹配服务,即发送方的发送速率与接收方应用程序的读取速率相匹配。
TCP 通过让发送方维护一个接收窗口来提供流量控制 。接收窗口用于给发送方一个指示---接收方还有多少可用的缓存空间。因为TCP 是全双工通信,在连接两端的发送方都各自维护一个接收窗口。TCP 不允许已分配的缓存溢出,因此接收缓存不能小于 从网络中到达的并且已放入主机接收缓存的数据流的最后一个字节的编号 与应用进程从缓存读出的数据流的最后一个字节的编号的差。
五、拥塞控制
慢启动
拥塞避免
一旦进入拥塞避免状态, cwnd 的值大约是上次遇到拥塞时的值的一半, TCP 无法每过一个 RTT 再将 cwnd 的值翻番,而是采用了一种较为保守的方法,每个 RTT 只将 cwnd 的值增加一个 MSS 。通用的方法是对于 TCP 发送方无论何时收到一个新的确认,就将 cwnd 增加 1/cwnd。
如果 MSS 是 1460 字节并且 cwnd 是 14 600 字节,则在一个 RTT 内发送 10 个报文段 。 每个到达 ACK (假定每个报文段一个 ACK)增加 1/ 10 MSS的拥塞窗口长度,因此在收到对所有 10 个报文段的确认后,拥塞窗口的值将增加了一个 MSS 。
当出现超时时, TCP 的拥塞避免算法行为相同 。 与慢启动的情况一样, ssthresh 的值被更新为 cwnd 值的一半,cwnd 的值被设置为 1 个 MSS。
三个冗余 ACK 事件:在这种情况下,网络继续从发送方向接收方交付报文段(就像由收到冗余 ACK 所指示的那样)。 TCP 对这种丢包事件的行为,相比于超时指示的丢包,应当不那么剧烈 。TCP 将 ssthresh 的值记录为 cwnd 的值的一半,将 cwnd 的值减半,接下来快速重传并进入快速恢复状态。
快速恢复
在快速恢复中,对于引起 TCP 进入快速恢复状态的缺失报文段,对收到的每个冗余的ACK, cwnd 的值增加一个 MSS。当丢失报文段的一个 ACK 到达时, TCP 在降低cwnd后进入拥塞避免状态 。 如果出现超时事件,快速恢复在执行如同在慢启动和拥塞避免中相同的动作后,迁移到慢启动状态:当丢包事件出现时, cwnd 的值被设置为 1 个MSS ,并且 ssthresh 的值设置为 cwnd 值的一半 。 快速恢复是 TCP 推荐的而非必需的。
六、TCP连接状态
CLOSED:初始状态。
LISTEN:服务器处于监听状态。
SYN_SEND:客户端socket执行CONNECT连接,发送SYN包,进入此状态。
SYN_RECV:服务端收到SYN包并发送服务端SYN包,进入此状态。
ESTABLISH:表示连接建立。客户端发送了最后一个ACK包后进入此状态,服务端接收到ACK包后进入此状态。
FIN_WAIT_1:终止连接的一方(通常是客户机)发送了FIN报文后进入。等待对方FIN。
CLOSE_WAIT:(假设服务器)接收到客户机FIN包之后等待关闭的阶段。在接收到对方的FIN包之后,自然是需要立即回复ACK包的,表示已经知道断开请求。但是本方是否立即断开连接(发送FIN包)取决于是否还有数据需要发送给客户端,若有,则在发送FIN包之前均为此状态。
FIN_WAIT_2:此时是半连接状态,即有一方要求关闭连接,等待另一方关闭。客户端接收到服务器的ACK包,但并没有立即接收到服务端的FIN包,进入FIN_WAIT_2状态。
LAST_ACK:服务端发动最后的FIN包,等待最后的客户端ACK响应,进入此状态。
TIME_WAIT:客户端收到服务端的FIN包,并立即发出ACK包做最后的确认,在此之后的2MSL时间称为TIME_WAIT状态。