UDP (User Datagram Protocol) 用户数据报协议
- 无连接,发送之前不需要建立连接。
- 不保证可靠交付。
- UDP面向报文,发送方的UDP对应用层交下来的报文,既不合并也不拆封,添加首部后直接交付网络层。接收方的UDP对网络层交上来的报文去掉UDP首部后直接交付给应用层。所以应用层在使用UDP时必须选择大小合适的报文。如果太长,网络层会分段导致降低网络层的效率,如果太短,网络层首部占比太大会浪费网络资源。
- UDP没有拥塞控制。当很多主机都向网络发送高速率实时视频流时,网络可能出现拥塞导致大家都无法正常接收。
- UDP支持一对一、一对多、多对一的和多对多的交互通信。
- UDP首部开销少,只有 8 字节
UDP首部字段定义,长度单位:字节
| 字段 | 源端口 | 目的端口 | 长度 | 校验和 |
|---|---|---|---|---|
| 长度 | 2 | 2 | 2 | 2 |
源端口: 源端口号。在需要对方回信时选用。不需要时可用全0。
目的端口: 目的端口号,终点交付报文时用到。
长度: UDP用户数据报的长度,其最小值是8(仅有首部)
校验和: 检测UDP用户数据报在传输中是否有错。有错就丢弃。
校验和计算方式
- 在UDP用户数据报之前增加12个字节的伪首部
- 校验和字段填入 全零
- 如果总字节数是奇数,在尾部添加一个全零字节。
- 每2个字节一组。按照二进制反码求和的方式计算所有组的和。
- 计算结果求反码后填入校验和字段。
伪首部定义如下,长度单位:字节
| 字段 | 源IP地址 | 目的IP地址 | 0 | 协议 | UDP长度 |
|---|---|---|---|---|---|
| 长度 | 4 | 4 | 1 | 1 | 2 |
第3个字段是全零字段,应该是为了填充为偶数个字节。
协议字段 UDP 是17,TCP 是 6
TCP (Transmission Control Protocol) 传输控制协议
- TCP是面向连接的运输层协议,应用程序使用TCP协议之前必须先建立TCP连接,数据传输完毕后必须释放已建立的TCP连接。
- 每一条TCP连接只有2个端点,TCP只支持一对一通信。
- TCP提供全双工通信。TCP允许通信双方的应用程序在任何时候都可以发送数据。
- TCP两端都设有发送缓存和接收缓存用来临时存放通信数据。
- TCP面向字节流,虽然应用程序和TCP的交互是一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。
- TCP和UDP在发送报文时所采用的方式完全不同。TCP并不关心应用进程一次把多长的报文发送到TCP的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP发送的报文长度是应用进程给出的)
-
如果应用进程传送到TCP缓存的数据块太长,TCP就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP也可以等待积累有足够多的字节后再构成报文段发送出去
- TCP连接的端点叫做套接字(socket),socket = IP:Port
- TCP连接 = {socket1,socket2} = {IP1:Port1,IP2:Port2}
停止等待协议
- 每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
- 设置一个超时计时器,如果在规定时间内没有收到对方确认,就重传。
- 自动重传请求ARQ (Automatic Repeat reQuest)。
- 发送完一个分组后保存副本,等收到确认后才清除。
- 接收方收到重复的分组后,丢掉,并且也发送一个确认。
- 这种协议信道利用率太低,只收到确认才继续发送,所以大部分时间信道是空闲的。
连续ARQ协议
- 像流水线一样传输,一次发送多个分组,不用发一个等一个。
- 发送方维持发送窗口,发送窗口是一个range,位于发送窗口内的分组可以连续发送。
- 发送方每收到一个确认就把窗口向前移动一个分组的位置。
- 接收方一般采用累积确认。例如收到确认编号100,表示编号100以及之前的所有分组都正常接收了。
- 优点:不用每个分组都确认。
- 缺点:发送方无法得知确认编号之后的分组有没有收到,只好重传。
TCP首部
- TCP的全部功能都体现在它首部中各字段的作用。
- TCP首部20字节固定长度,后面有4n个字节根据需要而增加。
TCP报文段的首部字段定义如下:长度单位:byte
| 字段 | 源端口 | 目的端口 | 序号 | 确认号 | 偏移保留控制 | 窗口 | 校验和 | 紧急指针 | 选项 |
|---|---|---|---|---|---|---|---|---|---|
| 长度 | 2 | 2 | 4 | 4 | 2 | 2 | 2 | 2 | 4n |
偏移、保留、控制位定义如下,长度单位:bit
| 字段 | 数据偏移 | 保留 | URG | ACK | PSH | RST | SYN | FIN |
|---|---|---|---|---|---|---|---|---|
| 长度 | 4 | 6 | 1 | 1 | 1 | 1 | 1 | 1 |
源端口和目的端口号和UDP相似。
序号
- 占4字节,范围 0 ~ 2^32-1, 共有 2^32 即 42,9496,7296 个序号。
- TCP是面向字节流的,序号是针对字节的编号。
- 在TCP中传输的每个字节都顺序编号,序号达到最大值时,从 0 开始。
- 首部中的序号是指本报文数据部分首字节的编号。
- 要传送字节的起始序号在建立连接时设定。
确认号
- 占4字节,期望收到对方下一个报文段的第一个数据字节的序号。
- 确认号为N,表示序号到 N-1 为止的所有数据已正确接收。
数据偏移、保留、控制位
数据偏移
- 占4位,指示数据部分距离起始位置的偏移。说白了就是首部长度。
- 偏移量的单位是4字节。偏移量最大值15,所以首部长度最大为 60 字节。
- 固定首部是20字节,所以选项长度最大是 40 字节。
保留
- 占6位,保留为今后使用。
控制位说明本报文段的性质
| 字段 | 说明 |
|---|---|
| 紧急 URG | (urgent)URG=1时表明本报文中有紧急数据要传送,告诉接收方这段数据很急排队太浪费时间了得插队,接收方会把紧急数据插入到本报文段的最前面。URG需要配置紧急指针使用 |
| 确认 ACK | (acknowledge) 仅当ACK = 1 时确认号字段才有效。当 ACK = 0 时,确认号无效。TCP规定,在连接建立后所有传送的报文段都必须把ACK置1 |
| 推送 PSH | PSH (push),当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应。在这种情况下,TCP就可以使用推送(push)操作。这时,发送方TCP把PSH置1,并立即创建一个报文段发送出去。接收方TCP收到PSH = 1的报文段,就尽快地(即“推送”向前)交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。 |
| 复位 RST | (reset)当RST = 1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。RST置1还用来拒绝一个非法的报文段或拒绝打开一个连接。RST也可称为重建位或重置位。 |
| 同步 SYN | (SYNchronization) 在连接建立时用来同步序号。当SYN = 1而ACK = 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使SYN = 1和ACK = 1。因此,SYN置为1就表示这是一个连接请求或连接接受报文。 |
| 终止 FIN | (FINis,意思是“完”、“终”) 用来释放一个连接。当FIN = 1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。 |
窗口
- 占2字节,窗口值区间 0 ~ 2^16-1,即 0 ~ 65535。
- 窗口值告诉对方,从本报文段确认号算起,接收方目前允许发送方发送的数据量。
- 例如:确认号121,窗口值2000,表示发送方接收完120号字节后还有2000字节的缓存空间。
- 窗口值作为接收方让发送方设置其发送窗口的依据,窗口值是经常变化的。
校验和
- 和UDP一样,只是协议字段值为 6
紧急指针
- 占2字节,URG = 1 时才有意义。
- 紧急指针指出紧急数据的末尾在报文段中的位置。
- 紧急数据结束后就是普通数据。
- 窗口值为 0 也可以发送紧急数据。
选项
- 长度可变,最长可达 40 字节。
- 当没有选项时,TCP首部为 20 字节
TCP 可靠传输的实现 - 以字节为单位的滑动窗口
- 发送方设有发送缓存,发送方应用程序把字节流写入发送缓存。
- 发送缓存维持一个发送窗口,发送窗口是发送缓存中的一个区间。
- 发送窗口设有三个指针 P1,P2,P3。
- P1 指向窗口后沿,序号小于 P1 的字节是已发送且已收到确认。
- P2 指向下一个要发送的字节,区间 [P1,P2) 的字节是已发送但未收到确认。
- P3 指向滑动窗口最大序号+1,即指向不允许发送的序号最小的字节。
- 区间 [P2,P3) 的字节是允许发送但尚未发送的。
- 发送方根据新收到的确认号和窗口调整发送窗口。
- 发送窗口的大小还会根据网络状况适当调整。
- 当发送窗口已满而且超过一定时间仍未收到对方的确认,发送方会重传未收到确认的数据。
- 已收到确认的数据从发送缓存中删除。发送窗口的后沿和缓存重合。
- 接收方设有接收缓存,接收缓存中有个接收窗口。
- 接收方对没有按序达到的字节暂时存放在接收窗口中,等全部达到后再交付应用层。
- TCP要求接收方必须有累积确认的功能,这样可以减小传输开销。
- 接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。
- TCP标准规定,确认推迟的时间不应超过0.5秒,时间长会引起不必要的重传。
- 捎带确认实际上并不经常发生,因为大多数应用程序不同时在两个方向上发送数据。
TCP - 超时重传时间的选择
重传很简单,但是如何设置一个合适超时重传时间是个复杂的问题。设置的时间太短,会引起不必要的重传,增大网络的负荷,设置的时间太长,会使网络空闲时间增大,降低传输效率。
报文往返时间RTT,一个报文从发出到收到确认的时间差。
加权平均往返时间 RTT<font size:8>s</font>,第一测量取RTT,以后每测量一次RTT,按照如下公式计算:
新RTTS = (1-α) * 旧RTTS + α * 新RTT
RFC 2988推荐的α值为1/8
超时计时器设置的超时重传时间 RTO (RetransmissionTime-Out)应略大于上面得出的加权平均往返时间RTTS
RTO = RTTS + 4 * RTTD
而RTTD是RTT的偏差的加权平均值,它与RTTS和新的RTT样本之差有关
RTTD = (1-β) * 旧RTTD + β * | RTTS - 新RTT |
β的推荐值是1/4,即0.25
报文重传后计算RTT样本无法得知是之前的报文的确认还是重传后的报文的确认。
Karn提出了一个算法:在计算加权平均RTTS时,只要报文段重传了,就不采用其往返时间样本。这样得出的加权平均RTTS和RTO就较准确。
按照这种方式如果报文段的时延突然增大了很多,超时时间无法更新也不行。
改进的Karn算法:报文段每重传一次,就把超时重传时间RTO增大一些。典型的做法是取新的重传时间为2倍的旧的重传时间。当不再发生报文段的重传时,才根据公式计算超时时间。
选择确认SACK
-
如果要使用选择确认SACK,那么在建立TCP连接时,就要在TCP首部的选项中加上“允许SACK”的选项,而双方必须都事先商定好。如果使用选择确认,那么原来首部中的“确认号字段”的用法仍然不变。只是以后在TCP报文段的首部中都增加了SACK选项
-
在选项中最多只能指明4个字节块的边界信息
利用滑动窗口实现流量控制
发送方,接收方说多了容易乱,我们用AB来代替。假设A是发送方,B是接收方。A正在给B传送数据。
- A发一个数据报给B,序号是x ,长度 100。
- B收到后给A一个确认,并告诉我A:你发给我的序号x长度100的数据报我收到了,我还有200个字节的接收缓存空间,我想收到的下一个序号x + 100 + 1。
- A很听话,调整好自己的发送窗口,信心满满的发给B 序号x+100+1,长度200的数据报。
- B收到后,告诉A:我收到了,但是缓存满了,你先别发了。
- A听到B缓存满了,只好等待B有接收缓存后,再继续发。
- B有了缓存空间后,想告诉A我有缓存了,你继续发吧。但是该数据报丢失了。
- A无法收到,所以一直等啊等,等了很久后依然没收到。A心里想:B怎么没动静了,是不是睡着了?
- A等不及了,主动发起一个数据报询问B:你到底怎么回事了?这么长时间了,还没有缓存空间吗?
- B看到A主动来问了,赶紧告诉我A:我有缓存空间了。
- A看到B有缓存了,又开心的发数据了。
拥塞控制的方法
慢开始和拥塞避免
- 发送方维持一个叫做拥塞窗口 cwnd (congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口
- 只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
- 只要发送方没有按时收到应当到达的确认报文,就可以猜想网络可能出现了拥塞
- 现在通信线路的传输质量一般都很好,因传输出差错而丢弃分组的概率是很小的(远小于1 %)。
-
慢开始算法的思路是这样的。当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。经验证明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口cwnd设置为一个最大报文段MSS的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值。用这样的方法逐步增大发送方的拥塞窗口cwnd,可以使分组注入到网络的速率更加合理。
- 拥塞避免算法的思路是让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样,拥塞窗口cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
- 发送窗口应该为拥塞窗口和对方给出的接收窗口的最小值。
发送窗口 = min(rwnd,cwnd)
几种拥塞控制的方法
- 发送方维持一个叫做 拥塞窗口 cwnd (congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度
慢开始(slow-start)
- 第一次传输数据拥塞窗口值为单位1,之后每次传输窗口值加倍,即
窗口值 *= 2。
拥塞避免(congestion avoidance)
- 第一次传输数据拥塞窗口值为单位1,之后每次传输窗口值+1。
窗口值 += 1
快重传(fast retransmit)
- 接收方收到不连续的报文也就是发现了丢包,就赶紧告诉发送方,ack是收到的连续报文的最大序号。
- 每收到一个不连续的报文就告诉发送方一次。
- 发送方收到三次对同一个序号的重复确认,就立即发出丢失的报文,而不是等着超时重传。
快恢复
随机早期检测RED
- 不要等到路由器缓存队列满了再丢弃分组,这样的话会引引起全局同步,即所有TCP连接都进入慢开始。
- 而是等队列快要满的时候就以概率P进行丢包。