本文将介绍传输层的两个协议 TCP和UDP,系统讲解TCP可靠传输、拥塞控制、三次握手四次挥手。
常见问题
- UDP和TCP的区别
- 为什么需要三次握手、四次挥手,为什么挥手多一次?
- 拥塞控制的原理?
- 什么是SYN攻击?
传输层概述
传输层是面向通信过程的最高层了,它工作在终端设备,管理端到端的通信连接
在上一篇文章中讲过,通过网络层IP协议,解决了网络通信问题,可以把A计算机(浏览器)与B计算机(网站后台服务器)的通信看成 A --> 虚拟的互联网络 --> B,不管A与B运行的实例都是进程,因此传输层协议可以看做进程与进程之间的通信
与 Unix域套接字和共享内存只能在单机进程间通信不同, 网络通信可以在单机通信也可以跨网络通信
那么计算机如何识别是与哪一个进程进行通信呢? -- 端口
- 使用端口(Port)来标记不同的网络进程
- 端口(Port)使用16比特位表示(0~65535)
FTP | HTTP | HTTPS | DNS | TELNET |
---|
21 | 80 | 443 | 53 | 23 |
传输层主要就两种协议UDP和TCP
UDP
- UDP(User Datagram Protocol: 用户数据报协议)
- UDP是一个非常简单的协议
数据报(Datagram)
- 数据报(是UDP协议中一个非常重要的特征
- 数据报是应用层传输过来的完整数据
- 对这个数据UDP不对它进行任何处理(不合并、不拆分)
- 所以UDP数据的长度由应用层数据的长度决定
UDP协议被包裹在IP数据包中
UDP的首部
- 校验和 是检测UDP报文传输中是否出错(使用的是上一篇文章中说的CRC循环冗余检测算法)
对比IP协议头部 和 TCP协议头部,可以看出UDP非常简单
UDP的特点
- UDP是无连接协议
- UDP不能保证可靠的交付数据
- UDP是面向报文传输的(内容里面即是应用层数据)
- UDP没有拥塞控制
- 首部开销小 总共8字节
UDP的优缺点
- 数据包在传输过程中容易丢失
- 大文件会被拆分成很多小的数据包来传输,这些小的数据包会经过不同的路由,并在不同的时间到达接收端,而 UDP 协议并不知道如何组装这些数据包,从而把这些数据包还原成完整的文件。
- 虽说UDP不能保证数据的可靠性,但传输速度却非常快,所以UDP会关注速度,但对数据完整性要求没有那么严格的领域,如在线视频、互动游戏。
为了解决UDP丢包问题和无法传输大文件问题,引入了TCP协议。
TCP协议
- TCP(Transmission Control Protocol: 传输控制协议)
- TCP协议是计算机网络中非常复杂的一个协议
TCP数据包也是被包裹在IP协议中
TCP协议的特点
- TCP是面向连接的协议
- TCP的一个连接有两端(点对点的通信)
- TCP提供可靠的传输服务
- TCP协议提供全双工的通信
- TCP是面向字节流的协议
- 字节流 流入流出进程的字节序列
- 取出数据包中的某一段 (可能进行合并也可能进行分拆)进行传输
-
TCP协议的头部
- 序号
- 0至232−1
- 一个字节一个序号
- 代表传输的第一个字节的序号
- 确认号
- 和序号配合使用 也是 0至232−1 一个字节一个序号
- 代表期望收到的下一个数据的首字节序号
- 比如当前报文序号为501,数据报的长度100字节,那么确认号就是600
- 收到确认号 代表 前 确认号-1个字节的数据都已经收到
- 数据偏移
- 占4位:0~15,单位为:32位字
- 由于TCP首部有可选的TCP选项的存在,所以需要纪录数据偏离首部的距离
- 我们可以计算一下TCP首部有多少字节
- 20个固定字节 最大偏移 15*4 即 20-60字节
- TCP标记
- 占6位,每位各有不同的意义
- URG/ACK/PSH/PST/SYN/FIN
- 窗口
- 占16位:0 至 216−1
- 窗口指明允许对方发送的数据量
- 比如确认号501 窗口值1000,那么从501到1500这么多字节的数据都可以接受的
- 校验和 同UDP校验和
- 紧急指针
- TCP选项
- 填充,填充为32bit(字)的整数倍
TCP标记
标记 | 含义 |
---|
URG | Urgent: 紧急位,URG=1,表示紧急数据 |
ACK | Acknowledgement: 确认位,ACK=1,确认号才生效 |
PSH | Push: 推送位,PSH=1,尽快地把数据交付给应用层 |
PST | Reset: 重置位,RST=1,重新建立连接 |
SYN | Synchronization: 同步位,SYN=1 表示连接请求报文 |
FIN | Finish: 终止位,FIN=1 表示释放连接 |
头部有固定20个字节
可靠传输的基本原理
- TCP的可靠传输基于连续ARQ协议
- TCP的滑动窗口以字节为单位
对TCP选择重传的理解
- TCP报文丢失是指整个片段的丢失而不是丢失某个字节,这时候需要重传这一片段段的字节
- TCP将多个丢失的报文起止序列号放入 TCP选项中,告知发送端重新发送
- TCP选项最多40字节,每个序列号是4字节,也就是能存储10个序列号
- 每两个序列号是一个片段,一个片段可以由多个连续报文组成。
流量控制
通过窗口大小控制对方发送速率
我们在延伸一下
接收方告知发送方窗口为0后
发送方还是会一直等待,,,
接收方会对这些数据进行处理然后交给应用层。一段时间后它又可以接受消息了, 这时候接收方还可以给发送方发送消息说当前我的窗口可以接受1000字节数据了(rwnd=1000)。
假设该报文丢失了(TCP的可靠传输只针对数据,对特殊的消息是没有超时重传的机制)
接收方以为已经告知了发送方窗口调整的通知,也会继续等待
这是就形成死锁的情况
为了解决这个问题, TCP就引入了 坚持定时器, 这样就是窗口调整数据发生丢失 也不会造成死锁。
坚持定时器
- 当接收到窗口为0的消息,则启动坚持定时器
- 坚持定时器每隔一段时间发送一个窗口探测报文
拥塞控制
如何衡量网络用塞
- 以丢包作为依据
- 以探测带宽作为依据
由于TCP发送不定长字节流,所以天然的容易占满带宽。 网络带宽 = min(拥塞窗口,通告窗口)
慢启动
- 先发送一个报文
- 收到ack后再发送2个报文
- 收到2个ack确认,就发送4个报文
- 依次类推, 这个过程叫做慢启动
慢启动要和拥塞避免一起使用才行,否则会大量丢包
拥塞避免
- 达到慢启动阈值后以现行方式增加cwnd
- 当发现丢包后将将阈值调整为原来的一半,然后重新慢启动
- 达到阈值,以线性增长
快速重传
- 假设有一个大文件传输
- 当发生丢包时,接收端只能回复丢包前ack(前一个报的最大序列号)
- 发送端收到多个重复确认ack,不能再一直发送下去,因为接收端无法组装数据交付给应用端了。
- 如果从连续确认的ack开始重新发送,这样效率低,
- 应该只需发送丢失的部分数据包即可。
- 所以制定了个策略,当收到3个重复失序ACK时,不在等待定时器的触发,立刻基于快速重传机制重新发送报文段
快速恢复
- 当快速重传发生丢包时,一定需要重新慢启动吗?这样效率很低,
- 改进方案,适当的将网速降下来一点
BBR拥塞控制算法
2016年google提出了以测量驱动的算法,对TCP的拥塞控制有大幅提升。
Linux4.9内核引入,QUIC使用
- 网络丢包我们可以抽象为瓶颈路由器造成的
- 随着内存价格降低,路由器内存增大,可以缓存更多的报文,也造成RTT更高
- 使用以丢包为依据的算法会造成高时延和大量丢包
最佳控制点
- 有了最佳控制点, 当RTT增加时,我们就可以测量出带宽BW。
- 当然实际算法要复杂很多,网络带宽是会变化的、RTT有误差等
三次握手
我来翻译一下上图
SYN=1
表示请求建立连接
seq=x
seq=y
表示当前请求的首字节的序列号
ACK=1
表示我知道你的序列号是什么了
ack=x+1
ack=y+1
表示期望接收的下一个字节的序号
- 发送方是在第二次请求时建立连接,接收方是在第三次请求时建立连接
为什么需要三次握手
- 网上有一种说法 是 第一次握手确定用户端能发,第二次握手确定服务端能收能发,第三次握手是客户端能收。貌似有道理😇
- 其实不是这样的, 三次握手是为了解决已经失效的连接请求报文传送到对方,引起错误
- 因为TCP的丢包重传机制和网络延时问题,第一次发送的请求经过可很长时间才得到服务端,由于超时,客户端重发请求,第二次请求比第一次请求先到到服务端,此时服务端就建立了两个连接。
- 如果使用第三次握手 服务端收到响应后立即回复,客户端收到请求后建立了连接, 此时第一个跑的慢的报文也到达服务端了,服务端也回应了,由于客户端已经建立连接,会把后面的请求丢弃掉。
Q:TCP是否每次连接都需要三次握手?
- TCP有个机制 TFO(TCP fast open)快速连接
- 第一次握手
- SYN在fast open带上cookie
- 服务端产生cookie,在ACK返回改cookie给客户端
- 客户端存储cookie
- 之后
- 客户端带有cookie
- 服务端验证该cookie,有效,在返回ACK的同时,将数据直接返回给应用层,无需3次握手
- 少一次ACK的RTT(round-trip time,往返时延)
四次挥手
Q:为什么断开连接比建立连接多一次?
- 服务端接收到客户端的请求后,还有一些数据没发送完,先回应客户端我知道了吗,等处理完以后再告知客户端我已经准备好了可以关闭连接了。
等待定时器
- 等待定时器会等待2MSL后才关闭连接
- MSL是最大报文段寿命
- 一般MSL为2分钟,2MSL即4分钟
- 由于一个TCP占用一个端口,断开连接后该端口无法使用,只能等到2MSL才能再次使用这个端口
为什需要等待定时器?
- 在启动等待定时器时,最后一个报文服务端还没接收到,等待2MSL确保发送方的ACK可以到达接收方, 如果还没收到则服务端会重发
- 另外等待2MSL,确保所有报文都已经过期
TCP与UDP的区别
| TCP | UDP |
---|
是否连接 | 面向连接 | 无连接 |
是否可靠 | 可靠传输,流量控制、拥塞控制、重传机制 | 不可靠传输 |
连接对象个数 | 只能是一对一通信 | 支持一对一,一对多,多对一和多对多交互通信 |
传输方式 | 面向字节流 | 面向报文 |
首部开销 | 20 - 60 字节 | 8 字节 |
适用场景 | 适用于要求可靠传输的应用,例如文件传输、金融交易、MQ、可靠通信 | 适用于实时应用(IP 电话、视频会议、语音、直播等) |
SYN攻击
- 半连接 & 全连接
- 在三次握手中,当客户端发送SYN到服务端,服务端收到以后回复ACK和SYN,状态由LISTEN变为SYN_RCVD,此时这个连接就被推入了SYN队列,也就是半连接队列。
- 当客户端返回ACK, 服务端接收后,三次握手完成。这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列。
- SYN flood攻击 客户端短时间内大量发送SYN:
- 服务端的大量返回ACK,半连接池吃紧
- 接收不到客户端的ACK,服务端超时重发
如何解决?
- 服务端加大半连接池
- SYN cookie:在接受达到SYN后不立即分配资源,根据这个SYN算出一个cookie,在ACK中返回给客户端,客户端在ACK里加上cookie,校验合法后分配资源
套接字
- 使用端口(Port)来标记不同的网络进程
- 端口(Port)使用16比特位表示(0~65535)
域套接字与网络套接字