目录
TCP是传输层的重点协议,对于我们了解网络原理有着至关重要的作用。
一、 TCP协议段格式
TCP 协议
TCP ,即 Transmission Control Protocol ,传输控制协议。人如其名,要对数据的传输进行一个详细的控制。
1.1 TCP协议格式
- 源/目的端口号:表示数据是从哪个进程来,到哪个进程去
- 32位序号/32位确认号:后面详细给大家介绍
- 4位首部长度:表示该TCP头部有多少个32位bit(有多少个4字节),所以TCP头部最大长度是 15 * 4 = 60
- 6位标志位:
- 16位窗口大小:流量控制(下面我会详细介绍)
- 16位校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分
- 16位紧急指针:标识哪部分数据是紧急数据
- 40字节头部选项:暂时忽略
二、TCP原理 (基本机制/特性)
TCP 对数据传输提供的管控机制,主要体现在两个方面:安全和效率。这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。
2.1 确认应答机制
TCP将每个字节的数据都进行了编号,即为序列号,但是并不会真正的存储每个字节的序列号,只需要保存起始序号,通过TCP的报文长度来+ 起始序号得到最后一个字节的编号。
每一个 ACK 都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。
2.2 超时重传机制 (安全机制)
但是,主机 A 未收到 B 发来的确认应答,也可能是因为 ACK 丢失了:
那么,如果超时,这个时间如何确定?
TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。
2.3 连接管理机制(安全机制)
在正常情况下, TCP 要经过三次握手建立连接,四次挥手断开连接。
三次握手:
四次挥手:
服务端状态转化:
客户端状态转化:
为什么是TIME_WAIT的时间是2MSL?
关于CLOSE_WAIT
2.4 滑动窗口(效率机制)
刚才给大家介绍了确认应答策略,对每一个发送的数据段,都要给一个 ACK 确认应答。收到 ACK 后再发送下一个数据段。这样做有一个比较大的缺点,就是性能较差。尤其是数据往返的时间较长的时候。
既然这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。
那么如果出现了丢包,如何进行重传?这里分两种情况讨论:
情况一:数据包已经抵达,ACK被丢了。
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。
情况二:数据包丢失
这种机制被称为 "高速重发控制"(也叫 "快重传")。
2.5 流量控制(安全机制)
接收端处理数据的速度是有限的。如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等一系列连锁反应。
因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度。这个机制就叫做 流量控制。
接收端如何把窗口大小告诉发送端呢?在上面的 TCP 首部(报头)中,有一个 16位窗口字段,就是存放了窗口大小信息。那么问题来了, 16 位数字最大表示 65535 ,那么 TCP 窗口最大就是 65535 字节么?
2.6 拥塞控制(安全机制)
虽然 TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据。但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题。 因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵。在不清楚当前网络状态下,贸然发 送大量的数据,是很有可能引起雪上加霜的。 因此TCP 引入 慢启动 机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。
像上面这样的拥塞窗口增长速度,是指数级别的。"慢启动" 只是指初始时慢,但是增长速度非常快。 为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。此处引入一个叫做慢启动的阈值 ,当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。
少量的丢包,我们仅仅是触发超时重传,大量的丢包,我们就认为网络拥塞。当TCP通信开始后,网络吞吐量会逐渐上升,随着网络发生拥堵,吞吐量会立刻下降。 拥塞控制,归根结底是 TCP 协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的 折中方案。
2.7 延时应答(效率机制)
如果接收数据的主机立刻返回 ACK 应答,这时候返回的窗口可能比较小。
窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。
那么所有的包都可以延迟应答么?肯定也不是:
具体的数量和超时时间,依操作系统不同也有差异,一般N取2,超时时间取200ms。
2.8 捎带应答(效率机制)
在延迟应答的基础上,我们发现很多情况下,客户端服务器在应用层也是 " 一发一收 " 的。意味着客户端给服务器说了 "How are you"(请求) ,服务器也会给客户端回一个 "Fine, thank you"(响应),那么这个时候ACK 就可以搭顺风车,和服务器回应的 "Fine , thank you" 一起回给客户端
三、粘包问题
那么如何避免粘包问题呢?归根结底就是一句话,明确两个包之间的边界。
思考:对于UDP协议来说,是否也存在 "粘包问题" 呢?
四、TCP异常情况
- 进程终止:进程终止会释放文件描述符,仍然可以发送FIN。和正常关闭没有什么区别。
- 机器重启:和进程终止的情况相同。
- 机器掉电/网线断开:接收端认为连接还在,一旦接收端无写入操作,就可以发现连接已经不在了,就会进行reset。即使没有写入操作,TCP自己也内置了一个保活定时器(心跳包),会定期询问对方是否还在。如果对方不在,也会把连接释放。
五、TCP小结
为什么 TCP 这么复杂?因为要保证可靠性,同时又尽可能的提高性能。
可靠性:
- 校验和
- 序列号(按序到达)
- 确认应答
- 超时重发
- 连接管理
- 流量控制
- 拥塞控制
提高性能:
- 滑动窗口
- 快速重传
- 延迟应答
- 捎带应答
其他:
- 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)