15

私は現在、プロジェクト用の素朴なネットワーク コードを書いています。友人は、情報のパッケージをサーバーからすべてのクライアントに反復的に送信すると、クライアントの 1 つが適切に応答しないときに激しい遅延が発生する可能性があることをほのめかしました。 .

彼はトローリングで知られているので、サーバーがパッケージを追加するだけのキューを持ち、データを送信するためにスレッドによって読み取られる、クライアントにデータを送信する役割を担うセカンダリ スレッドを実装するとき、私はちょっと懐疑的でした。

よく考えてみた結果、Java Socket の OutputStream は実際に送信したいものを自分でキューに入れるので、事前にキューを作成する必要がなくなります。送信されたオブジェクトが受信されたというクライアントからの応答がない限り、サーバーがブロックしている場合にのみ、深刻な問題が発生する可能性があります。

ありがとう。

4

3 に答える 3

12

あなたの友人は正しいですが、それはプロトコルがどのように機能するかに関係があります。クライアントに送信される大幅に簡素化されたパケットを確認する必要があります。クライアントが応答しない場合 (着信データの読み取りに失敗した、コンピューターに負荷がかかっているなど)、サーバーは確認応答を受信せず、データの送信を停止します。TCP/IP に組み込まれたこのメカニズムは、通信の一方の端が大量のデータを送信するのを防ぎます。これにより、大量のデータを再送信する必要がなくなります。

Java では、これは への書き込みをブロックするものとして現れOutputStreamます。基礎となる TCP/IP スタック/オペレーティング システムは、クライアントが受信準備が整うまで、それ以上データを送信することを許可しません。

これで簡単にテストできます!接続を受け入れるが受信データの読み取りに失敗する単純なサーバーを実装しました。

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            final ServerSocket serverSocket = new ServerSocket(4444);
            final Socket clientSocket = serverSocket.accept();
            final InputStream inputStream = clientSocket.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}).start();

そして、4K バッチでできるだけ多くのデータを送信するだけのシンプルなクライアント:

final Socket client = new Socket("localhost", 4444);
final OutputStream outputStream = client.getOutputStream();
int packet = 0;
while(true) {
    System.out.println(++packet);
    outputStream.write(new byte[1024 * 4]);
}

95 回繰り返した後、クライアント ループが私のコンピューターでハングします (マイレージは異なる場合があります)。inputStreamただし、サーバースレッドから読み取ると、ループが延々と続きます。

于 2012-05-13T19:44:00.953 に答える
4

もちろん、ソケットに書き込むと、この書き込みはバッファリングされます。ソケット オブジェクトには、setSendBufferSize()このバッファ サイズを設定するメソッドがあります。書き込みをこのバッファにキャッシュできる場合は、もちろん、次のソケットですぐに反復できます。それ以外の場合は、このバッファーをクライアントにすぐにフラッシュする必要があります。したがって、フラッシュ中はブロックされます。バッファのフラッシュ中にブロックされるのを避けたい場合はSocketChannel、非ブロッキング I/O で a を使用する必要があります。とにかく、多くのソケットに同時に書き込むための最良のオプションは、すべての書き込みが同時に実行されるように、各ソケットを異なるスレッドで管理することです。

于 2012-05-13T19:54:48.517 に答える
2

OutputStream がブロックされています。おそらくいくらかのバッファリングがありますが、サーバーがバイトをまったく消費しない場合はあまり役に立ちません (固定バッファは最終的にいっぱいになります)。別のスレッドに書き込むか、nio などのより高度なものを使用する必要があります。

読み取り側では、 available() を使用してブロックを回避できます。書き込み側に一致する呼び出しがありません。あったらいいのに。

于 2012-05-13T19:49:09.710 に答える