3

Java(NIO)サーバー(Linuxを実行している)からクライアントに複数の大きなメッセージをすばやく連続して送信すると、パケットが切り捨てられるという厄介な問題があります。問題が発生するには、メッセージが大きく、非常に迅速に送信される必要があります。これが基本的に私のコードが行っていることです(実際のコードではありませんが、多かれ少なかれ何が起こっているのか):

//-- setup stuff: --
Charset charset = Charset.forName("UTF-8");
CharsetEncoder encoder = charset.newEncoder();
String msg = "A very long message (let's say 20KB)...";

//-- inside loop to handle incoming connections: --
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true);
sc.socket().setSendBufferSize(1024*1024);

//-- later, actual sending of messages: --
for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  sc.write(bb);
  bb.rewind();
}

したがって、パケットが十分に長く、可能な限り迅速に送信される場合(つまり、遅延のないこのようなループで)、もう一方の端では、次のような結果になることがよくあります。

[COMPLETE PACKET 1]
[COMPLETE PACKET 2]
[COMPLETE PACKET 3]
[START OF PACKET 4][SOME OR ALL OF PACKET 5]

データが失われ、パケットが一緒に実行され始めます。そのため、パケット5の開始(この例では)は、パケット4の開始と同じメッセージで到着します。切り捨てるだけでなく、メッセージを一緒に実行します。

これはTCPバッファまたは「ウィンドウサイズ」に関連していると思います。または、ここのサーバーは、OSやネットワークアダプタなどが処理できるよりも高速にデータを提供しているだけだと思います。しかし、どうすればそれをチェックして、それが起こらないようにすることができますか?sc.write()を使用するたびにメッセージの長さを減らしても、繰り返しを増やしても、同じ問題が発生します。短時間でのデータ量の問題に過ぎないようです。sc.write()が例外をスローしていることもわかりません(上記の例ではチェックしていませんが、テストにはあります)。

プログラムでデータの準備ができていないかどうかをプログラムで確認し、遅延を入れて、準備ができるまで待つことができれば幸いです。「sc.socket()。setSendBufferSize(1024 * 1024);」かどうかもわかりません。何らかの効果があります。または、Linux側でこれを調整する必要がある場合。SocketChannelを実際に「フラッシュ」する方法はありますか?不十分な回避策として、たとえば10KBを超えるメッセージを送信しようとしているときはいつでも、バッファリングされているものを明示的に完全に送信しようとすることができます(これは私のアプリケーションではそれほど頻繁ではありません)。しかし、バッファの送信を強制する(または送信されるまで待つ)方法はわかりません。助けてくれてありがとう!

4

5 に答える 5

6

sc.write() が一部またはすべてのデータを送信しない理由は多数あります。戻り値および/またはバッファに残っているバイト数を確認する必要があります。

for (int n=0; n<20; n++){
  ByteBuffer bb = encoder.encode(CharBuffer.wrap(msg+'\0'));
  if(sc.write(bb) > 0 && bb.remaining() == 0) {
    // all data sent
  } else {
    // could not send all data.
  }
  bb.rewind();
}
于 2009-01-31T17:47:38.803 に答える
5

次の戻り値をチェックしていません。

sc.write(bb);

これにより、書き込まれたバイト数が返されます。これは、バッファで使用可能なデータよりも少ない場合があります。nioがどのように機能するかにより、バイトバッファでremaining()を呼び出して、残っているものがあるかどうかを確認できます。

于 2009-01-31T16:30:13.937 に答える
3

私はNIOプログラミングを行っていませんが、Javadocによると、SocketChannelが非ブロッキングモード(あなたのように)で、ソケットの出力バッファーがいっぱいの場合、sc.write()はByteBuffer全体を書き込みません。

あなたはとても速く書いているので、あなたはあなたの接続を氾濫させている可能性が非常に高く、あなたのネットワークまたは受信機は追いつくことができません。

まだデータの準備ができていないかどうかをプログラムで確認できれば幸いです

sc.write()の戻り値をチェックして、出力バッファーがいっぱいかどうかを確認する必要があります。

于 2009-01-31T16:28:24.190 に答える
1

どのデータがどのパケットに入れられるかを制御できると思い込まないでください。

詳細はこちら:ソケットで新しいデータを監視し、そのデータを処理するための最良の方法は何ですか?

于 2009-01-31T18:15:30.947 に答える
1

非ブロッキング モードを使用しています: sc.configureBlocking( false );

ブロッキングをtrueに設定すると、コードはそのまま機能するはずです。送信数とループを確認するためにここで他の人が行った提案も機能します。

于 2009-03-25T13:01:18.013 に答える