理解UDP协议的首部校验和校验和

时间:2022-01-26 06:26:42

reference:

https://blog.csdn.net/qiuchangyong/article/details/79945630

https://seanwangjs.github.io/2017/10/19/udp-protocol-checksum.html

关于udp传输的不可靠性,用过这个的人都知道会丢包。具体细节可能就不清楚了,经过我的理解和总结,有以下两点:

1)udp包的大小可以达到64k,但实际上mtu大小只有1k多,如果直接发一个超过mtu大小的包,就会在协议层被分片,这样的问题是,如果只要有一个分片在传输中出错了即校验不正确(这是较容易发生的),整个传输的udp包就被丢弃。注意是整个而不是单个分片。这就是为什么发送udp包通常也是1k多大小的原因,rtp是在udp之上的协议,也考虑了这个问题。

2)实际上收到的数据都是经过校验的,不存在传错的问题,即在应用层调用udp传输时,不会出现发送了"ABCD",收到的却是"ABED"的情况,只有丢包或乱序的问题。而udp接收缓冲区过小也是造成丢包的原因,适当增大udp缓冲区能够降低丢包率。

在实际应用中,比如流媒体传输中,就要对接收的乱序的包进行重排(重排时间的长短又是一个关键),这时候发现某个包丢了(前提是要加一个序号,这个rtp包头里面有),还需要做重传的工作,然而udp协议就是为了传输的实时性而生的,所以这里面就有一个权衡的问题,既不能全部都重传,也不能不重传。如果全部都重传,那就和TCP没有区别了;如果不重传,丢包就会导致流媒体的不完整性,需要做的处理就是错误修复或隐藏,对于音频和视频都有一些算法,由于信息丢失,这些算法也只能做一些弥补,但对于无法进行重传的情况,比如播放基于udp的ts节目流,这些纠错算法的好坏就是关键。

理解UDP协议的首部校验和校验和

45 00 00 2e----4表示ip版本号为ip第4版;5表示首部长度为5个32 bit字长,即为20字节;00 2e表示ip总长度为46字节,其中ip数据部分为26字节。 
be 55 00 00----be 55表示标识符;00 00表示3 bit标志及13 bit片偏移量; 
7a 11 51 ac----7a表示ttl值为122;11表示协议号为17的udp协议;51 ac表示16 bit首部检验和值; 
de b7 7e e3----表示32 bit 源ip地址为222.183.126.227 
c0 a8 12 7a----表示32 bit 目的ip地址为192.168.18.122

-------------------------------------------------------------------------------------------

检验和计算: 
首先,把检验和字段置为0。 
45 00 00 2e 
be 55 00 00 
7a 11 00 00<----检验和置为0 
de b7 7e e3 
c0 a8 12 7a 
其次,对整个首部中的每个16 bit进行二进制反码求和,求和值为0x3_ae50,然后3+ae50=0xae53(这是根据源代码中算法 cksum = (cksum >> 16) + (cksum & 0xffff) 进行的 ) 
最后,ae53+51ac=ffff。因此判断ip首部在传输过程中没有发生任何差错。

"二进制反码求和" 等价于 "二进制求和再取反"
从源代码看,很关键的一点是二进制求出的和如果大于16位时所做的操作,用和值中高16位加上低16位的值作为最终的和值,然后再做取反运算.


对于TCP和UDP的数据报,其头部也包含16位的校验和,校验算法与IPv4分组头完全一致,但参与校验的数据不同。这时校验和不仅包含整个TCP/UDP数据报,还覆盖了一个虚头部。虚头部的定义如下:

0         7 8   15 16   23 24       31 
                    +--------+--------+--------+------------+
                    |             source address              |
                       +--------+--------+--------+---------+
                    |           destination address          |
                    +--------+--------+--------+------------+
                    |  zero  |protocol| TCP/UDP length  |
                    +--------+--------+--------+------------+

其中有IP源地址,IP目的地址,协议号(TCP:6/UDP:17)及TCP或UDP数据报的总长度(头部+数据)。将虚头部加入校验的目的,是为了再次核对数据报是否到达正确的目的地,并防止IP欺骗攻击(spoofing)。

理解UDP协议的首部校验和校验和

以及这段信息的十六进制表示

理解UDP协议的首部校验和校验和

有了以上这些内容,剩下的就是对照着最开始的图表来寻找各个参数的值了( wireshark 一个十分好用的功能就是点选上面的人类可读内容,其十六进制值会在下面高亮显示)。我用下表来表示

key human hex
Source 192.168.1.106 c0a8 016a
Destination 11.111.111.111 0b6f 6f6f
Protocol UDP(17) 11
Length 17 11
Source Port 63549 f83d
Destination Port 12345 3039
Length 17 11
Checksum 0xb12d b12d
Data hello UDP 6865 6c6c 6f20 5544 5000

然后就可以开始着手校验和的计算了,但在这之前还应注意,上表中有一项 Checksum ,这是发送方根据发送内容计算出来校验和,接受方需要根据收到的内容重新计算一遍校验和,然后再对比两者。所以在接收方计算时应该忽略这里 Checksum 项。

再次举例:

source ip

destination ip

protocol udp :固定为0x11

length*2 : length为:数据长度+8;

source port

destination port

data:从开头取16bit,当结尾为8bit,将低8bit设为0x00;当然0x00不能算为数据长度

上述计算完成后,将超过16bit的高位截取,加到低16bit中;再取反,即0xFFFF减去其即可。

C0A8_0003
C0A8_000A
11
10 (8+8)
10
8000
8000
1122_3344_5566_7788
----------------------
3_92E2
-------
92E5
------
6D1A