校驗和算法分析【轉】



        另外關於二進制反碼求和運算需要說明的一點是,先取反後相加與先相加後取反,得到的結果是一樣的!(事實上我們的編程算法裏,幾乎都是先相加後取反。)

2. 校驗和算法的實現


講了什麼是二進制反碼求和,那麼校驗和的算法實現就簡單多了。廢話少說,直接上代碼:

[cpp]view plaincopy

 

1        //計算校驗和 

2        USHORT checksum(USHORT *buffer,int size) 

3        

4            unsigned long cksum=0; 

5            while(size>1) 

6            { 

7                cksum+=*buffer++; 

8                size-=sizeof(USHORT); 

9            } 

10          if(size) 

11          { 

12              cksum+=*(UCHAR *)buffer; 

13          } 

14          //32位數轉換成16 

15          while (cksum>>16) 

16              cksum=(cksum>>16)+(cksum &0xffff); 

17          return (USHORT) (~cksum); 

18      

buffer是指向需校驗數據緩存區的指針,size是需校驗數據的總長度(字節爲單位)

4~13行代碼對數據按16bit累加求和,由於最高位的進位需要加在最低位上,所以cksum必須是32bitunsigned long,高16bit用於保存累加過程中的進位;另外代碼10~13行是對size爲奇數情況的處理!

14~16行代碼的作用是將cksum16bit的值加到低16bit上,即把累加中最高位的進位加到最低位上。這裏使用了while循環,判斷cksum16bit是否非零,因爲第16行代碼執行的時候,仍可能向cksum的高16bit進位。

有些地方是通過下面兩條代碼實現的:

cksum = (cksum >> 16) + (cksum & 0xffff); 
cksum +=(cksum >>16);

這裏只進行了兩次相加,即可保證相加後cksum的高16位爲0,兩種方式的效果一樣。事實上,上面的循環也最多執行兩次!

17行代碼即對16bit數據累加的結果取反,得到二進制反碼求和的結果,然後函數返回該值。


3. 爲什麼使用二進制反碼求和呢?

        好了,最後一個問題,爲什麼要使用二進制反碼來計算校驗和呢,而不是直接使用原碼或者補碼?

這個問題我想了很久,由於水平有限實在弄不明白,於是在百度上一陣狂搜,什麼都沒有(不知道是百度不給力,還是大家都不關注這個問題呢?)。果斷換google,敲了3個關鍵詞:why checksum tcp,嘿嘿結果第二篇就是我想要的文章了!!!

先把鏈接給大家吧:http://www.netfor2.com/checksum.html

這篇文章主要介紹二進制反碼求和(the 1's complement sum)與補碼求和(the 2's complement sum)的區別,另外還說明了在TCP/IP校驗和中使用反碼求和的優點。

It may look awkword to use a 1'scomplement addition on 2's complement machines. This method however has its ownbenefits.

Probably the most important is that itis endian independent. Little Endian computers store hex numbers with the LSBlast (Intel processors for example). Big Endian computers put the LSB first(IBM mainframes for example). When carry is added to the LSB to form the 1'scomplement sum (see the example) it doesn't matter if we add 03 + 01 or 01 +03. The result is the same.

Other benefits include the easiness of checking the transmission and the checksum calculation plus a variety of ways to speed up the calculation by updating only IP fields that have changed.

       上面是原文的一部分,說明TCP/IP校驗和中使用反碼求和的一些優點:

        a.不依賴系統是大端還是小端。即無論你是發送方計算或者接收方檢查校驗和時,都不需要調用htons 或者 ntohs,直接通過上面第2節的算法就可以得到正確的結果。這個問題你可以自己舉個例子,用反碼求和時,交換16位數的字節順序,得到的結果相同,只是字節順序相應地也交換了;而如果使用原碼或者補碼求和,得到的結果可能就不相同!

        b.計算和驗證校驗和比較簡單,快速。說實話,這個沒怎麼看明白,感覺在校驗和計算方面,原碼或者補碼求和反而更簡單一些(從C語言角度),在校驗和驗證上面,通過一樣的算法判斷結果是否爲全0,確實要方便一些,所以可能從綜合考慮確實反碼求和要簡便一些。另外,IP報文在傳輸過程中,路由器經常只修改TTL字段(減1),此時路由器轉發該報文時可以直接增加它的校驗和,而不需要對IP整個首部進行重新計算。當然,可能從彙編語言的角度看,反碼求和還有很多高效的地方,這裏就不在深入追究了~~~


結語:本來一個不怎麼注意的地方,深入探究一下竟然發現這麼多東西。學習算法其實沒有必要抱着《算法導論》一頁一頁地啃(嘿嘿,哥也有一本哦),我更喜歡從TCP/IP協議或LInux內核原理中去探究算法以及實現思想,這樣反倒更有趣,而且這裏面的一些算法和思想相當經典,慢慢體會,必然受益匪淺!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章