理解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