Javaでチェックサムを実装する方法【サンプルコード付き解説】
チェックサム(Checksum)は、ネットワーク通信などでデータの破損や改ざんを検出するために用いられる、代表的な誤り検出手法の一つです。送信側がデータから計算したチェックサム値を受信先へ送り、受信側で同じ計算を行って照合することで、データが正しく届いたかどうかを確認できます。
本記事では、Javaを使ってインターネットチェックサム方式(1の補数和による方式)を実装する方法を、実際のコードとともに詳しく解説します。
実装例
import java.util.*;
public class Demo{
public static void main(String args[]){
Scanner my_scan = new Scanner(System.in);
System.out.println("Enter the input string ");
String my_in = my_scan.next();
int my_checksum = generate_checksum(my_in);
System.out.println("The checksum that has been generated is " + Integer.toHexString(my_checksum));
System.out.println("Enter the data that needs to be sent to the receiver ");
my_in = my_scan.next();
System.out.println("Enter the checksum that needs to be sent to the receiver ");
my_checksum = Integer.parseInt((my_scan.next()), 16);
receive(my_in, my_checksum);
my_scan.close();
}
static int generate_checksum(String s){
String my_hex_val = new String();
int x, i, my_checksum = 0;
for (i = 0; i < s.length() - 2; i = i + 2){
x = (int) (s.charAt(i));
my_hex_val = Integer.toHexString(x);
x = (int) (s.charAt(i + 1));
my_hex_val = my_hex_val + Integer.toHexString(x);
System.out.println(s.charAt(i) + "" + s.charAt(i + 1) + " : " + my_hex_val);
x = Integer.parseInt(my_hex_val, 16);
my_checksum += x;
}
if (s.length() % 2 == 0){
x = (int) (s.charAt(i));
my_hex_val = Integer.toHexString(x);
x = (int) (s.charAt(i + 1));
my_hex_val = my_hex_val + Integer.toHexString(x);
System.out.println(s.charAt(i) + "" + s.charAt(i + 1) + " : "+ my_hex_val);
x = Integer.parseInt(my_hex_val, 16);
} else {
x = (int) (s.charAt(i));
my_hex_val = "00" + Integer.toHexString(x);
x = Integer.parseInt(my_hex_val, 16);
System.out.println(s.charAt(i) + " : " + my_hex_val);
}
my_checksum += x;
my_hex_val = Integer.toHexString(my_checksum);
if (my_hex_val.length() > 4){
int carry = Integer.parseInt(("" + my_hex_val.charAt(0)), 16);
my_hex_val = my_hex_val.substring(1, 5);
my_checksum = Integer.parseInt(my_hex_val, 16);
my_checksum += carry;
}
my_checksum = generate_complement(my_checksum);
return my_checksum;
}
static void receive(String s, int my_checksum){
int gen_checksum = generate_checksum(s);
gen_checksum = generate_complement(gen_checksum);
int syndrome = gen_checksum + my_checksum;
syndrome = generate_complement(syndrome);
System.out.println("The value of syndrome is " + Integer.toHexString(syndrome));
if (syndrome == 0){
System.out.println("Data has been received without any errors");
} else {
System.out.println("An error was encountered in the received data");
}
}
static int generate_complement(int my_checksum){
my_checksum = Integer.parseInt("FFFF", 16) - my_checksum;
return my_checksum;
}
}入力
sample sample b2c8
出力結果
Enter the input string sa : 7361 mp : 6d70 le : 6c65 The checksum that has been generated is b2c8 Enter the data that needs to be sent to the receiver Enter the checksum that needs to be sent to the receiver sa : 7361 mp : 6d70 le : 6c65 The value of syndrome is 0 Data has been received without any errors
このように「sample」という文字列からチェックサム「b2c8」が生成され、同じデータとチェックサムを受信側で検証すると、シンドローム値が「0」となり、エラーなく受信できたことが確認できます。
コードの仕組みを詳しく解説
main関数:プログラムのエントリーポイント
Demoクラスにはmain関数が定義されています。ここではScannerインスタンスを生成してユーザーからの入力を受け付け、入力された文字列をgenerate_checksum関数に渡してチェックサムを生成します。その後、受信側を想定したデータとチェックサムの入力を受け取り、receive関数で整合性の検証を行います。
generate_checksum関数:チェックサムの生成
generate_checksum関数では、新しい文字列インスタンスを作成し、チェックサムの初期値として0を設定します。引数として渡された文字列を2文字ずつ順番に走査し、各文字を整数値に変換したうえで16進数表現に直していきます。隣接する2文字の16進数値を連結して1つのワードを構成し、それを累積的に加算することで、1の補数和の計算を行います。
文字列の長さが偶数の場合は、最後の残り2文字についても同様に整数化・16進数化して加算します。一方、長さが奇数の場合は、余った1文字の前に「00」を連結してパディングを行い、同じく加算の対象とします。
すべての加算が完了した後、合計値が16進数で5桁以上になった場合はオーバーフローが発生しているため、最上位桁を「キャリー(繰り上がり)」として切り離し、残りの4桁の値にキャリーを加算して折り返します。これはインターネットチェックサム特有の「折り返し加算(wrap-around carry)」の処理です。最後にgenerate_complement関数を呼び出して1の補数を求め、完成したチェックサムを返します。
receive関数:受信データの検証
receive関数は、受信したデータに対して再度generate_checksum関数を呼び出し、新たにチェックサムを計算します。その結果と、送信側から受け取ったチェックサム値を加算し、さらにgenerate_complement関数で補数を取ることで「シンドローム(診断値)」を求めます。
シンドロームが0であれば、データには一切エラーがないことを意味します。逆に0以外の場合は、伝送途中でデータが破損したことを検出できます。
generate_complement関数:1の補数の計算
generate_complement関数は、渡されたチェックサム値を16進数の「FFFF」(10進数で65535)から減算することで、1の補数を計算します。この補数演算によって、正しいデータの組み合わせであれば合計が必ずFFFFになるという性質を利用した検証が可能になります。
まとめ
この実装例では、文字列を2文字ずつ16進数ワードに変換し、キャリーを折り返しながら加算し、最後に1の補数を取ることでチェックサムを生成しています。TCP/IPプロトコルでも採用されているこの手法は、シンプルでありながら効果的な誤り検出手段です。ネットワークプログラミングやデータ通信の学習にお役立てください。
-
JavaとOpenCVで長方形を描画する方法|Imgproc.rectangle()の使い方を徹底解説
Java用OpenCVライブラリの org.opencv.imgproc パッケージには、Imgproc クラスが含まれています。画像に長方形を描画するには、このクラスが提供する rectangle() メソッドを使用します。 このメソッドは以下のパラメータを受け取ります。 Matオブジェクト:長方形を描画する対象となる画像を表します。 Pointオブジェクト(2つ):描画する長方形の対角となる2つの頂点座標を表します。 Scalarオブジェクト:長方形の色をBGR形式で指定します。 整数値:長方形の枠線の太さを指定します(デフォルト値は1)。 サンプルコード 以下は、白紙の画像に長方形を
-
JavaとOpenCVで線を描画する方法をわかりやすく解説
Java版OpenCVライブラリのorg.opencv.imgprocパッケージには、Imgprocというクラスが含まれています。画像に直線を描画するには、このクラスが提供するline()メソッドを呼び出します。 line()メソッドのパラメータ line()メソッドは、以下のパラメータを受け取ります。 Matオブジェクト ― 直線を描画する対象となる画像を表します。 Pointオブジェクト2つ ― 直線の始点と終点の座標を表します。 Scalarオブジェクト ― 直線の色をBGR形式で指定します。 整数値 ― 直線の太さを指定します(デフォルト値:1)。 サンプルコード 以下は、白紙の画