Java Standard Edition アプリケーションを開発しています。
アプリケーションのユーザーには仮名があります。
私のアプリケーションはサーバーとして使用できます。
このような場合、アプリケーションのクライアント インスタンスからの接続を受け入れるためにスレッドが実行されます。
接続が受け入れられると、クライアント ユーザーがリストに登録されます。
サーバー スレッドは、定期的にユーザーのリストをすべてのクライアント インスタンスにブロードキャストします。
クライアント インスタンスは、サーバー インスタンスから送信されたデータから独自のユーザー リストを更新します。
体系的ではない問題があります。
私の問題はTCPプロトコルに関連していると思います。
テスト中、サーバーとクライアントのインスタンスは同じコンピューターで実行されています。
RawCap と Wireshark を使用して、127.0.0.1 で送受信されるデータのフレームをキャプチャしました。
問題が発生するシナリオは次のとおりです。
ユーザU1はアプリケーションをサーバとして実行する。
ユーザー U2 はアプリケーションをクライアントとして実行し、サーバー インスタンスに接続します。
サーバー インスタンスは接続を受け入れます。
接続を確立するために使用されるフレームをキャプチャしました。
tns-adv はクライアント インスタンスを意味し、tns-server はサーバー インスタンスを意味します。
フレームは次のとおりです。tns-adv > tns-server [SYN] Seq=0
tns-server > tns-adv [SYN, ACK] Seq=0 Ack=1
tns-adv > tns-server [ACK] Seq=1 Ack=1クライアント インスタンスは、U2 の仮名をサーバー インスタンスに送信します。
U2のペンネームは「もい」。
仮名の後には、復帰文字と改行文字が続きます。
フレームは次のとおりです。tns-adv > tns-server [PSH、ACK] Seq=1 Ack=1 Len=5
サーバー インスタンスは、U2 の仮名を受け取ります。TCP 確認応答がクライアント インスタンスに自動的に送信されます。
フレームは次のとおりです。tns-server > tns-adv [ACK] Seq=1 Ack=6
サーバー インスタンスは、U2 を独自のユーザー リストに登録します。
U2 は 2 番目の位置に登録されています。
サーバーは U2 の位置 (2) をクライアント インスタンスに送信します。
この位置の後には、キャリッジ リターンとライン フィード文字が続きます。
フレームは次のとおりです。tns-server > tns-adv [PSH、ACK] Seq=1、Ack=6 Len=3
クライアント インスタンスは U2 の位置を受け取り、U2 を独自のリストの 2 番目の位置に登録します。
TCP 確認応答がサーバー インスタンスに自動的に送信されます。
フレームは次のとおりです。tns-adv > tns-server [ACK] Seq=6 Ack=4
サーバー インスタンスは、クライアント インスタンスに通知を送信します。
通知は、クライアント インスタンスがまだ使用可能であることを証明するデータを送り返すようにクライアント インスタンスに通知するために使用されます。
通知は「【DISPO?】」です。
通知の後には、キャリッジ リターンとライン フィード文字が続きます。
フレームは次のとおりです。tns-server > tns-adv [PSH、ACK] Seq=4 Ack=6 Len=11
TCP 確認応答は、クライアント インスタンスからサーバー インスタンスに自動的に送信されます。
しかし、次の Java コマンドはクライアント インスタンスでブロックします。 bufferedReader.readLine()
このコマンドは、サーバー通知を受け取ることになっています。
サーバー インスタンスで例外がキャッチされ、そのメッセージは次のとおりです。接続がリセットされました。
接続をリセットするフレームをキャプチャしました。
しかし、このフレームがクライアント インスタンスから送信される理由がわかりません。
フレームは次のとおりです。tns-adv > tns-server [RST、ACK] Seq=6 Ack=15
編集:コメントに応じて、次のコードスニペットを追加しました
クライアント インスタンスのスレッドで実行される関連する Java コマンド
// Connect to the server instance.
socket = new Socket(adrIpv4, numPortSvr);
// Instance an object to read data sent by the server instance.
inputStream = socket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
bufferedReader = new BufferedReader(inputStreamReader);
// Instance an object to send data to the server instance.
outputStream = socket.getOutputStream();
outputStreamWriter = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter);
// Send my pseudonym to the server instance.
// Frame sent : Frame 4.
bufferedWriter.write(monJoueur.getPseudonyme());
bufferedWriter.newLine();
bufferedWriter.flush();
// Wait for my position in the user list from the server instance.
// Frame received : Frame 6.
chaine = bufferedReader.readLine();
// Register myself into this instance's user list at the position received from the server instance.
partie.setJoueurAtPosition(monJoueur, position);
// Wait for an availability request from the server instance.
// Frame expected : Frame 8.
// That command blocks and the reset frame 9 is sent to the server instance.
chaine = bufferedReader.readLine();
サーバー インスタンスのスレッドで実行される関連する Java コマンド
// Create the server socket.
// Allocate automatically a free port number.
socketServeur = new ServerSocket(0);
partie.setSocketServeur(socketServeur);
// Set the max time the server instance can wait for a connection from a client instance : 500 milliseconds.
socketServeur.setSoTimeout(ThreadCreerPartie.DUREE_ATTENTE_CONNEX);
// Wait 500ms max for a connection from a client instance.
socket = socketServeur.accept();
// Instance an object to read data sent by the client instance.
inputStream = socket.getInputStream();
inputStreamReader = new InputStreamReader(inputStream);
bufferedReader = new BufferedReader(inputStreamReader);
// Instance an object to send data to the client instance.
outputStream = socket.getOutputStream();
outputStreamWriter = new OutputStreamWriter(outputStream);
bufferedWriter = new BufferedWriter(outputStreamWriter);
// Wait for the client's pseudonym.
// Frame received : Frame 4.
chaine = bufferedReader.readLine();
// Registrer the client into this instance's user list at the first available position.
positionJoueur = partie.inscrireJoueur(socket, chaine);
// Send the client's position in the user list to the client instance.
// Frame sent : Frame 6.
bufferedWriter.write(String.valueOf(positionJoueur));
bufferedWriter.newLine();
bufferedWriter.flush();
// Send an availability request to the client instance.
// Frame sent : Frame 8.
bufferedWriter.write("[DISPO ?]");
bufferedWriter.newLine();
bufferedWriter.flush();
// Receive the availability proof from the client instance.
chaine = null;
chaine = bufferedReader.readLine(); //< Raises an exception with the message : Connection reset.