1. 程式人生 > >理解UDP協議的首部校驗和校驗和

理解UDP協議的首部校驗和校驗和

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節目流,這些糾錯演算法的好壞就是關鍵。

 

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)。

 

以及這段資訊的十六進位制表示

有了以上這些內容,剩下的就是對照著最開始的圖表來尋找各個引數的值了( 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