1. 概要

ネットワークを介してデータを送受信するために、私たちはしばしばソケットを使用します。 ソケットは、IPアドレスとポート番号の組み合わせにすぎず、特定のマシンで実行されているプログラムを一意に識別できます。

このチュートリアルでは、ソケットを介して送信されたデータを読み取る方法を示します。

2. ソケットからのデータの読み取り

ソケットプログラミングの基本的な理解があると仮定しましょう。

次に、サーバーがリッスンしているポートでデータを読み取る方法について詳しく説明します。

まず、 ServerSocket、Socket、およびDataInputStream変数を宣言して初期化する必要があります。

ServerSocket server = new ServerSocket(port);
Socket socket = server.accept();
DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

ソケットをラップすることを選択したことに注意してください InputStream で DataInputStream。 これにより、テキストの行とJavaプリミティブデータ型を移植可能な方法で読み取ることができます。

これは、受信するデータのタイプがわかっている場合は、これで便利です。 readChar()、readInt()、readDouble()、readLine()などの特殊なメソッドを使用できます。 

ただし、データの種類と長さが事前にわからない場合は、困難な場合があります。

その場合、代わりに、下位レベルの read()関数を使用して、ソケットからバイトのストリームを取得します。 しかし、このアプローチには小さな問題があります。取得するデータの長さとタイプをどのようにして知ることができますか?

次のセクションでこのシナリオを調べてみましょう。

3. ソケットからのバイナリデータの読み取り

バイト単位でデータを読み取る場合、サーバーとクライアント間の通信用に独自のプロトコルを定義する必要があります。 定義できる最も単純なプロトコルはTLV(Type Length Value)と呼ばれます。 これは、ソケットに書き込まれるすべてのメッセージがTypeLengthValueの形式であることを意味します。

したがって、送信されるすべてのメッセージを次のように定義します。

  • 文字列のように、データ型を表す1バイト文字
  • データの長さを示す4byte整数
  • そして、その長さがちょうど示された実際のデータ

クライアントとサーバーが接続を確立すると、各メッセージはこの形式に従います。 次に、各メッセージを解析し、特定のタイプのnバイトのデータを読み取るコードを記述できます。

String メッセージを使用した簡単な例を使用して、これを実装する方法を見てみましょう。

まず、 readChar()関数を呼び出してデータの型を読み取り、次に readInt()関数を呼び出してデータの長さを読み取る必要があります。

char dataType = in.readChar();
int length = in.readInt();

その後、受信したデータを読み取る必要があります。 ここで注意すべき重要な点は、read()関数が1回の呼び出しですべてのデータを読み取ることができない場合があることです。 したがって、whileループでread()を呼び出す必要があります。

if(dataType == 's') {
    byte[] messageByte = new byte[length];
    boolean end = false;
    StringBuilder dataString = new StringBuilder(length);
    int totalBytesRead = 0;
    while(!end) {
        int currentBytesRead = in.read(messageByte);
        totalBytesRead = currentBytesRead + totalBytesRead;
        if(totalBytesRead <= length) {
            dataString
              .append(new String(messageByte, 0, currentBytesRead, StandardCharsets.UTF_8));
        } else {
            dataString
              .append(new String(messageByte, 0, length - totalBytesRead + currentBytesRead, 
              StandardCharsets.UTF_8));
        }
        if(dataString.length()>=length) {
            end = true;
        }
    }
}

4. データを送信するためのクライアントコード

そしてクライアント側のコードはどうですか?実際、それは非常に簡単です:

char type = 's'; // s for string
String data = "This is a string of length 29";
byte[] dataInBytes = data.getBytes(StandardCharsets.UTF_8);

out.writeChar(type);
out.writeInt(dataInBytes.length);
out.write(dataInBytes);

クライアントが行っているのはこれだけです。

5. 結論

この記事では、ソケットからデータを読み取る方法について説明しました。 特定のタイプのデータを読み取るのに役立つさまざまな関数を調べました。 また、バイナリデータの読み取り方法も確認しました。

このチュートリアルの完全な実装は、GitHubにあります。