大致了解TCP
TCP协议属于传输层,同为传输层的协议除了TCP还有一个UDP协议
传输层的两个协议:
UDP(User Datagram Protocol),用户数据报协议TCP(Transmission Control Protocol),传输控制协议
现在简单总结一下两者的区别
| TCP | UDP | |
|---|---|---|
| 连接性 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输,不丢包 | 不可靠传输,尽最大努力交付,可能丢包 |
| 首部占用空间 | 大 | 小 |
| 传输速率 | 慢 | 快 |
| 资源消耗 | 大 | 小 |
| 应用层协议 | HTTP、HTTPS、FTP、SMTP、DNS | DNS |
| 应用场景 | 浏览器、文件传输、邮件发送 | 音视频通话、直播 |
TCP的数据格式
TCP首部字段:
-
源端口和目的端口均占用两个字节 -
序号- 占4个字节
- 在传输过程中每个字节都有一个编号
- 这个字段仅在建立连接后才有意义,表示这次传给对方的TCP数据部分第一个字节的编号
-
确认号- 占4个字节
- 这个字段同样仅在建立连接后才有意义,表示期望下次对方传过来的TCP数据部分第一个字节的编号
-
数据偏移- 占4个字节
- 取值范围:0x0101 - 0x1111,即十进制的5~15
- 乘以4可以得到首部大小,首部大小范围是20字节到60字节
-
保留:有些资料认为保留字段只占3位,标志字段占有9位 -
标志位:-
URG(紧急字段),当且仅当URG字段为1时才表示紧急指针有效,表明当前报文有紧急数据,应当尽快传送 -
ACK,当ACK为1时才表示确认号字段有效 -
PSH表示要求对方数据尽快达到应用层 -
RST(RESET),当RST为1时表示连接中出现严重问题,必须释放链接,再重新建立链接 -
SYN(Synchronization)- 当
SYN为1,ACK为0时表示这是一个建立链接的请求 - 若对方同意链接,当回复
SYN为1,ACK为1
- 当
-
FIN(Finish)当FIN为1时,表明数据已经发送完毕,要求释放链接
-
-
窗口:- 占2个字节
- 此字段有流量控制的功能,用以告知对方下一次允许发送的数据大小(单位是字节)
-
紧急指针:占2个字节,仅当标志位的URG字段为1时,紧急指针才有效 -
检验和:占2个字节,计算内容:伪首部 + 首部 + 数据,伪首部占用12字节,仅在计算检验和时起作用,并不会传递给网络层
此图是检验和的内容
由上述分析可以看出TCP首部固定长度为20字节,但是可以扩充的为40字节,总计最大为60字节,并且其中首部中有4位表示的数据偏移字段准确记录了首部大小,但是和UDP不同的是,UDP首部有个16位的字段记录了整个UDP报文段的长度,其中包含首部和数据,但是,TCP的首部中仅仅有个4位的字段记录了TCP报文段的首部长度,并没有字段记录TCP报文段的数据长度。
针对上述问题:
UDP首部中占16位的长度字段是冗余的,纯粹是为了保证首部是4字节对齐- 另外传输层数据的大小可以推算出来:传输层的数据长度 = 网络层的总长度 – 网络层的首部长度 – 传输层的首部长度
TCP的可靠连接
都知道UDP是不可靠传输可能会出现丢包等情况,但是TCP是一种可靠连接,靠的就是停止等待ARQ(Automatic Repeat–request)自动重传协议来实现
停止等待ARQ(Automatic Repeat–request)
从上图可以看出,在没有任何差错的情况下,发送端发送一个数据包,接收方收到后会发送一个确认信息过来,发送方拿到确认信息后才会接着发送,简而言之是发送一个数据包后要收到一个确认后才会接着发送下一个数据包,重复这个过程。那么在一个数据包发送后,规定时间内发送方没有收到接收方发送的确认报文,会产生超时重传。
这个方法是可以保证可靠连接,但是很明显这是一个串行发送数据的过程,所以效率上会很慢,所以接下来在这个基础上会做一些优化,产生了连续ARQ协议+滑动窗口协议
连续ARQ协议+滑动窗口
可以看出,原来是一个包一个包的发送,但是连续ARQ协议+滑动窗口发送窗口会包含几个数据包,也就是几个数据包一起发送,接收方收到后回复一个收到最后一个分组的确认回来,然后窗口会下移到下一组数据。
如果接收窗口能接收4个包,但是发送方只发了2个包,那么这时接收方会等待一定时间,如果还是没有接收到数据包,这时会发送确认收到2个包给到发送方。
这里会有一个问题,如果发送的一组(M1、M2、M3、M4)中有一个包M2没有收到,是不是会重新发送(M2、M3、M4)这组数据呢?此时需要看接收方是否使用了SACK(选择确认)
SACK(Selective acknowledgment选择确认)
如果发送序列中间某个数据包丢失(比如M1、M2、M3、M4、M5中的M3丢失了),TCP会通过重传最后确认的分组后续的分组(最后确认的是M2,会重传M3、M4、M5),这样的话会导致降低了效率,因为M4、M5可能已经是收到了的。
这时SACK技术可以解决这些问题,告诉发送方哪些数据丢失,哪些数据已经提前收到,例如上述的问题,使用SACK技术后就会只发送丢失的M3而不会发送已经收到的M4、M5。
SACK信息会放在TCP首部的选项部分:
Kind:占1字节。值为5代表这是SACK选项Length:占1字节。表明SACK选项一共占用多少字节Left Edge:占4字节,表示左边界Right Edge:占4字节,表示右边界- 一对边界信息会占用8字节,
TCP首部的选项信息最多只有40字节,所以SACK最多携带4组边界信息((40 - 2) / 8 = 4)
TCP发送信息的过程整体如下图所示
疑问
为什么选择在传输层就将数据“大卸八块”分成多个段,而不是等到网络层再分片传递给数据链路层?
- 可以提高重传的性能
- 首先可靠传输是在传输层进行控制的,如果在传输层不分段,一旦出现数据丢失,整个传输层的数据都得重传,会严重影响效率,相反如果在传输层分了段,一旦出现数据丢失,只需要重传丢失的那些段即可
TCP流量控制
为什么要有流量控制?
- 如果接收方的缓冲区已经满了,但是发送方仍然不停的在发送数据,那么会导致很多数据包被丢弃,造成资源浪费
- 所谓流量控制是让发送方发送的速率不要太快
流量控制的原理:
- 通过确认报文中
窗口字段大小来调整发送方的发送速率 - 发送方的发送窗口大小不能超过接收方给出窗口大小
流量控制中的一种特殊情况:
- 当发送方收到接收窗口的大小为0时,发送方就会停止发送数据
- 当接收方有一些空间的时候,会发送一个接收窗口的大小为非0的报文,但是发送了丢失
- 这样会导致发送方和接收方陷入僵局
解决方案:
- 发送方收到接收窗口为0的报文时停止发送数据
- 另外开启一个定时器,每隔一段时间会询问接收方最新的窗口大小信息
在上述分析中可以看到TCP首部中有一个字段窗口,在连接的过程中发送方和接收方会沟通一些信息,其中就包括接收方能接收数据的大小以及发送方发送数据的大小
可以看出在建立连接的三次握手过程中,前两次的首部大小均大于20字节,也就是在选项部分会告诉对方一些额外的信息,第三次的时候沟通完毕,所以首部的大小回归正常的20字节大小,不再有额外的附加信息。
数据链路层规定的以太网帧大小最大是1500字节,那么网络层包最大应该是1500字节减去网络层首部大小20字节,1480字节是网络层最大的数据大小,则传输层数据大小最大应该是1480字节减去最小首部20字节,得到1460字节,但是注意1460字节是理论值。
TCP的拥塞控制
上面分析了流量控制,可以看出流量控制只是一种点对点的方案即发送方和接收方之间的一种平衡,但是网络中存在无数对接收方和发送方的关系,所以同时也要考虑整个网络中的通信质量情况,由此引入了拥塞控制这个概念。
几个重要字段:
MSS(Maximum Segment Size):每个段最大的数据大小cwnd(congestion window):拥塞窗口rwnd(receive window):接收窗口swnd(send window):发送窗口,发送窗口是取拥塞窗口和接收窗口两个值中的最小值
拥塞控制:
- 防止过多的数据注入到网络中,避免网络中路由器或者链路过载
- 拥塞过程是一个全局性的过程,涉及所有的主机、路由器,以及与降低网络传输性能有关的所有因素
拥塞控制的几个方法:
慢开始拥塞避免快重传快速恢复
慢开始
通过控制拥塞窗口大小来控制发送窗口大小,初始值较小,随着数据包被确认后开始依次呈现指数级递增,例如下图
拥塞避免
在经历过慢开始过程后,当拥塞窗口达到一个阈值后,便会不再以指数级增加而是改为线性级,拥塞避免的作用就是使拥塞窗口缓慢增加,防止网络过早出现拥塞,故拥塞避免又被称为加法增大。当拥塞窗口通过线性增长的过程后只要出现网络堵塞的现象,就会把慢开始的阈值减少为拥塞峰值的一半,同时重新进行慢开始的过程,具体过程如下图所示
所以当网络频繁出现频繁堵塞时,慢开始的阈值就会减少的很快。
快重传
之前在分析TCP的可靠连接的时候我们知道了如果一个数据包在发送过程发生了丢失,那么在一段时间后会触发超时重传,但是这里讲述的快重传与超时重传不同的点在于,快重传是发生在接收方只要收到一个失序的分组后会立即发送重复确认,而不是要等到自己发送数据时才进行确认(这里指发送的ACK确认,例如连续ARQ中要等到一组数据都完成后的操作),对于发送方来说,只要连续收到三个重复确认(总共四个相同的确认),就应当立即重传接收方未收到的数据段,而不是接着等待到重传计时器到期后才重传。
快恢复
当发送方连续收到三个重复确认此时说明网络拥堵,把慢开始的阈值减少到拥塞峰值的一半,但是此时并不再执行慢开始算法,而是将拥塞窗口的值设置为慢开始的阈值,并且直接开始拥塞避免算法,即线性增加的过程,如下图