題目與要求
編寫一個計算機程序用來計算一個文件的 16 位效驗和。最快速的方法是用一個 32 位的 整數來存放這個和。記住要處理進位(例如,超過 16 位的那些位),把它們加到效驗和中。
要求:
1)以命令行形式運行:check_sum infile
其中 check_sum 爲程序名,infile 爲輸入數據文件名。
2)輸出:數據文件的效驗和
附:效驗和(checksum)
參見 RFC1071 - Computing the Internet checksum
- 原理:把要發送的數據看成 16 比特的二進制整數序列,並計算他們的 和。若數據字
節長度爲奇數,則在數據尾部補一個字節的 0 以湊成偶數。
29 - 例子:16 位效驗和計算,下圖表明一個小的字符串的 16 位效驗和的計算。
爲了計算效驗和,發送計算機把每對字符當成 16 位整數處理並計算效驗和。如果效驗和大於 16 位,那麼把進位一起加到最後的效驗和中.
檢驗和原理
檢驗和原理,即爲IP數據報首部校驗和,其計算採用16位二進制反碼求和算法。
步驟:
將數據報首部按字(16位)進行反碼運算求和,獲取的和取反碼後即爲檢驗和。發送端發送數據報時將檢驗和帶上,接收端接受時,再次計算檢驗和,若結果爲0則保留;否則丟棄該數據報。
參考代碼
public class Main {
public static void main(String[] args) {
try {
if(args == null || args.length != 1 ) {
System.out.println("使用方式:java class名 文件路徑");
} else if(args.length == 1){
System.out.println("讀取"+args[0]+"...");
System.out.println("檢驗碼爲:"+check_sum(args[0]));
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("出現異常,已退出!");
}
}
/**
* 計算文件校驗和
* @param path 文件路徑
* @return 校驗和
*/
public static String check_sum(String path) throws IOException {
// 計算總和,
int sum = 0;
// 讀取文件流
File file = new File(path);
FileInputStream inputStream = new FileInputStream(file);
// 字節數組,2個字節,代表16位
byte[] b = new byte[2];
// 讀取的字節數
int n;
while((n = inputStream.read(b)) != -1) {
if(n == 1) {
// 讀取字節數爲奇數,補齊8個0(1字節)
sum += b[0] << 8;
} else {
// 讀取字節數爲偶數
sum += (b[0] << 8) + b[1];
}
}
inputStream.close();
// 進位處理
if(sum > 0xffff) {
sum = (sum / 65536 + sum % 65536);
}
// 取反碼 java的int佔32位,而校驗碼爲16位
return Integer.toHexString(~sum).substring(4);
}
}
關於文件讀取的問題:這裏建議用字節流讀取,因爲java中的String是2個字節的,也就是16位。而題目這裏要求的是兩個字組成16位。用字符流讀取不僅要進行字節的轉換,還容易出錯。使用其他編程語言實現也同理,建議使用字節流。
運行
首先在桌面創建一個文本文件:
然後打開命令行編譯運行程序:
java Main
中的Main
爲程序入口的類
檢驗運算結果
71FC: 0111 0001 1111 1100
取反: 1000 1110 0000 0011
即: 8 e 0 3
模擬客戶端接收:
4865+6C6C+6F20+776F+726C+642E+8E03(檢驗碼) = 2FFFD
2FFFD進行反碼處理:2FFFD / 0x10000 + 2FFFD % 0x10000 = 0xFFFF
取反:0x0000,即爲0,數據檢驗成功!