TCP协议详解
3.1 TCP服务的特点
-
TCP协议通信的双方必须先建立连接,然后进行数据的读写。双方必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输;
-
TCP连接必须是全双工的,即双方数据的读写可以通过一个连接完成。完成数据交换后,通信双方必须断开连接以释放系统资源;
-
TCP协议的连接是一对一的,不适合广播和多播;
-
当发送端应用程序连续执行多次写操作时,TCP模块先将这些数据放入TCP发送缓冲区中。当TCP模块真正开始发送数据时,发送缓冲区中这些等待发送的数据时,发送缓冲区数据被封装成一个或多个TCP报文段发出。因此,TCP模块发出的TCP报文个数和应用程序的写操作次数之间没有固定的数量关系;
-
当接收端接收到TCP报文后,将其按照序号依次放入TCP接收缓冲区中,并通知应用程序读取数据;
-
发送端执行的写操作次数和接收端执行的读操作之间没有数量关系,都是字节流的概念:应用程序对数据的发送和接收没有边界限制。
-
TCP和UDP提供的服务区别:
-
TCP传输具有可靠性:TCP协议采用发送应答机制;TCP协议采用超时重传机制;TCP协议对TCP报文段会进行重排、整理,再交付给应用层。
3.2 TCP头部结构
TCP头部结构包含固定头部结构和头部选项。
3.2.1 TCP固定头部结构
- 16位端口号:告知主机报文段源端口和目的端口。客户端一般使用临时端口号,服务器一般使用知名服务端口号;
- 32位序号:一次TCP通信过程中某个传输方向上字节流的每个字节的编号。
- 4位头部长度:标识该TCP头部有多少个32bit字。
- 6位标志位:URG标志(标识紧急指针是否有效);ACK标志(表示确认号是否有效);PSH标志(提示接收端应用程序应从TCP接收缓冲区读取数据);RST标志(要求对方重新建立连接);SYN标志(标识请求建立一个连接);FIN标志(标识通知对方本端要关闭连接);
- 16位窗口大小:TCP控制流量的手段,告诉对方本段TCP接收缓冲区还能容纳多少字节的数据;
- 16位校验和:发送端填充,接收端使用CRC算法进行校验查看在传输过程中是否损坏;
- 16位紧急指针:证的偏移量,和序号字段的值相加后标识最后一个紧急数据的下一字节的序号。
3.2.2 TCP头部选项
TCP头部的最后一个选项字段是一个可变长的可选信息,其最多包含40字节。
3.3 TCP连接的建立和关闭
3.3.1 使用tcpdump观察TCP连接的建立和关闭
$ sudo tcpdump -i eth0 -nt '(src 192.168.1.109 and dst 192.168.1.108) or (src 192.168.1.108 and dst 192.168.1.109)'
$ telnet 192.168.1.109 80
TCP连接的建立和关闭时序图
- 第一个TCP报文段包含SYN标志。它是一个同步报文段,即客户端向服务器发起连接请求。
- 第二个TCP报文段也是同步报文段,表明服务器同意与客户端建立连接,帮发送自己的ISN值。同时对第一个同步报文段进行确认,即将第一个报文段的序号值加1。同步报文段没有携带任何应用程序数据,但需要占用一个序号值。
- 第三个TCP报文段是客户端对第二个同步报文段的确认,至此TCP建立。上述为TCP三次握手。
- 从第三个TCP报文段开始,tcpdump输出的序号值和确认值都是相对初始ISN值的偏移。
- 第四个TCP报文段包含FIN标志,是一个结束报文段,表明客户端要求关闭连接,此报文段和同步报文段一样,需要占用一个序号值。
- 服务器使用第五个TCP报文段确认结束报文段,并紧接着服务器发送TCP报文段6。客户端使用TCP报文段7给予确认。
- 在连接关闭的过程中,客户端先发送结束报文段,执行主动关闭,服务器则执行被动关闭。
3.3.2 半关闭状态
TCP连接是全双工的,允许两个方向的数据传输被独立关闭。
服务器和客户端判断对方是否关闭连接的方法是:read系统调用返回0(收到结束报文段)
3.3.3 连接超时
3.4 TCP状态转移
TCP连接的任意一端在任意时刻都属于某一状态,当前状态可通过netstat
命令查看。
(粗虚线表示经典服务器端连接的状态转移;粗实线表示典型的客户端连接的状态转移)
3.4.1 TCP状态转移总图
服务器状态转移过程:
- 服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接;
- 服务器一旦监听到某个连接请求,将该连接放入内核等待队列中,并向客户端发送带SYN标志的确认报文段。此时进入SYN_RCVD状态;
- 服务器成功接收客户端发送回的确认报文段,进入ESTABLISHED状态,此状态是连接双方都能进行双向数据传输的状态;
- 当客户端主动关闭连接时,服务器通过返回确认报文段进入CLOSE_WAIT状态;
- 通常,服务器检测到客户端关闭连接后,会给客户端发送一个结束报文段来关闭连接,进入LAST_ACK状态,等待客户端对结束报文段的最后一次确认,一旦确认完成,连接彻底关闭。
客户端状态转移过程:
- 客户端通过connect主动与服务器建立连接。connect系统调用首先给服务器发送一个同步报文段,使连接进入SYN_SENT状态;
- connect调用失败将使连接返回初始的CLOSED状态。若客户端收到服务器的同步报文段和确认,连接转移至ESTABLISHED状态;
- 当客户端执行主动关闭时,其想服务器发送一个结束报文段,痛失进入FIN_WAIT_1状态;
- 此时客户端收到服务器专门用于确认的确认报文段,则连接转移至FIN_WAIT_2状态,此时服务器处于CLOSE_WAIT状态;
- 此时如果服务器也关闭连接,则客户端将基于确认并进入TIME_WAIT状态。
3.4.2 TIME_WAIT状态
客户端连接在收到服务器的结束报文段之后,并没有进入CLOSED状态,而是转移到TIME_WAIT状态。此时,客户端连接要等待2MSL的时间,才能完全关闭。
TIME_WAIT状态存在原因主要有:
- 可靠地终止TCP连接
- 保证让迟来的TCP报文段又足够的时间被识别并丢弃
3.5 复位报文段
在特殊条件下,TCP连接的一端会向另一端发送携带RST标志的报文段,即复位报文段,以通知对方关闭连接或重新建立连接。
3.5.1 访问不存在的端口
当客户端程序访问一个不存在的端口时,目标主机将给它发送一个复位报文段。
当客户端程序向服务器的某个端口发起连接,若该端口仍处于TIME_WAIT状态的连接所占用时候,客户端也将收到复位报文段。
3.5.2 异常终止连接
TCP提供了异常终止一个连接的方法:给对方发送一个复位报文段。一旦发送复位报文段,则发送端所有排队等待的数据都将被丢弃。
3.5.3 处理半打开连接
如果客户端或服务器向处于半打开状态的连接写入数据,此时对方则回应一个复位报文段。
半打开状态是指:服务器或客户端关闭或异常终止了链接,而对方没有收到结束报文段,此时还维持着原来的链接。而此时服务器或客户端即使重启,也没有该链接的任何信息。
3.6 TCP交互数据流
TCP报文段所携带的应用程序按照长度分为两种:交互数据和成块数据。交互数据包含字节介绍,其对实时性要求较高;成块数据一般长度为TCP报文段允许的最大数据长度,其对传输效率较高。
Nagle算法:要求一个TCP连接的通信双方在任意时刻都最多只能发送一个未被确认的TCP报文段,在该TCP报文段的确认到达之前,不能发送其他TCP报文段。另一方面,发送方在等待确认的同时手机本段需要发送的微量数据,并在确认来到时候以一个TCP报文段将它们全部发出,从而极大地减小网络上微小TCP报文段的数量。
3.7 TCP成块数据流
3.8 带外数据
某些传输层协议具有带外数据的概念,用于迅速通告对方本段发生的重要事件,其相比于普通数据有更高的优先级,应该总是立即被发送。
TCP没有真正的带外数据,其可利用头部中的紧急指针标志和紧急指针两个字段,给应用程序提供一种紧急方式。
3.9 TCP超时重传
Linux有两个重要的内核参数与TCP超时重传相关: /proc/sys/net/ipv4/tcp_retriesl 和 /proc/sys/net/ipv4/tep_retries2。前者指定在底层IP接管之前TCP最少执行的重传次数,默认为3;后者指定放弃连接前TCP最多执行的重传次数,默认为15。
3.10 拥塞控制
3.10.1 拥塞控制概述
- TCP拥塞控制四个部分:慢启动、拥塞避免、快速重传、快速恢复。
- 拥塞控制算法在Linux下有多种实现:reno算法、vegas算法和cubic算法。
- 拥塞控制的最终受控变量是发送端向网络一次连续写入的数据量,即SWND(发送窗口)。发送端最终以TCP报文段来发送数据,因此SWND限制了发送端能连续发送TCP报文段的数量。TCP报文段的最大长度称为SMSS(发送者最大段大小)
3.10.2 慢启动和拥塞避免
- 慢启动算法的理由:TCP模块刚开始发送数据时,并不知道网络的实际情况,需要用一种试探的方式平滑的增加CWND的大小。
- 发送端判断拥塞发生的依据:传输超时(TCP重传定时器溢出);接收到重复的确认报文段。
3.10.3 快速重传和快速恢复
略
3.10.4 拥塞控制流程整理
慢开始、拥塞避免、快重传、快恢复
- 发送放维持一个“拥塞窗口”的变量,该变量与接收窗口共同决定了发送者的发哦那个窗口。发送方的发送窗口上限值应当为接收方窗口rwnd喝拥塞窗口cwnd两个变量中较小的一个;
- 慢 开 始 : 慢开始: 慢开始:当主机开始发送数据时,为避免一下子将大量字节涌入网络中,造成或增加拥塞,发送方选择发送1字节的试探报文;
- 当收到第一个字节的数据的确认后,就发送2个字节的报文,从此一次递增2的指数级;
- 最后会到达一个提前预设的“慢开始门限sstresh”,此时遵循以下算法:
若 cwnd < sstresh,停止使用慢开始算法;
若 cwnd > sstresh,停止使用慢开始算法,改用拥塞避免算法;
若 cwnd = sstresh,既可以使用慢开始算法,又可以使用拥塞避免算法; - 拥 塞 避 免 算 法 : 拥塞避免算法: 拥塞避免算法:每经过一个往返时间RRT就将发送方的拥塞窗口+1,让拥塞窗口缓慢增大,按照线性规律增长;
- 当出现网络拥塞,将慢开始门限ssthresh设置为出现拥塞时的发送窗口的一半(但不能小于2),然后将cwnd设为1,开始执行慢开始算法;
-
快
重
传
机
制
:
快重传机制:
快重传机制:
1、接收方建立机制:如果一个包丢失,则对后续的包继续发送针对该包的重传请求;
2、一旦发送方接收三个一样的确认,就知道该报出现了错误,立即重传该包;
3、此时执行 快 恢 复 算 法 : 快恢复算法: 快恢复算法:
a、慢开始的门限变为出现拥塞时的一半;
b、拥塞窗口cwnd设为上面变化后的慢开始的门限值;
c、执行拥塞避免算法(高起点,线性增长);