2009-07-09 29 views
0

Javaで簡単なアプリケーションを書いた。そこには2つのノードがあり、それぞれに着信接続をリスンするポートに対してServerSocketが開いている。ノードはそれぞれ2つのスレッドを実行し、最初のメッセージの送信時に作成された永続的なTCPソケットを介して他のノードに1000のメッセージを送信します。ただし、ノードはすべて1000のメッセージを受信しません。 1つは850を受け取り、もう1つは650を受け取ります。この数は、複数の実行で一定に保たれる傾向があります。Java:複数のスレッド対ソケット

public void SendMsg(String dest, Message myMsg) { 
    Socket sendsock = null; 
    PrintWriter printwr = null; 
    try { 
     if(printwr == null) { 
      sendsock = new Socket(dest, Main.rcvport); 
      printwr = new PrintWriter(sendsock.getOutputStream(), true); 
     } 
     String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n"; 
     printwr.print(msgtosend); 
    } catch (UnknownHostException ex) { 
     System.out.println(ex); 
     //DO: Terminate or restart 
    } catch (IOException ex) { 
     System.out.println(ex); 
     //DO: Terminate or restart 
    } 
} 

パフォーマンスは私が buffwr =新しいBufferedWriterの(printwr)にも を使用する場合は改善し、buffwr.writeを使用しているようだ(...):

送信するコードは以下の通りです。 printwr.print(...)の代わりに、データ損失の完全な解決策ではありません。パケットが配信されなかったことを示す例外はないので、送信者に応じて、すべて正常に送信されました。

次のように受信側では、受け入れられた接続が処理されます。

BufferedReader inbuff = new BufferedReader(new InputStreamReader(incoming.getInputStream())); 

     while(running) { 
      String rcvedln = inbuff.readLine(); 
      if(rcvedln != null) { 
       count++; 
       System.out.println(count); 
      } 
     } 

が問題を引き起こしている可能性があり、読者と作家が使用されている方法に問題はありませんか?ありがとう。

答えて

1

ストリームを消去するためにPrintWriterをクローズしていますか?

} finally { 
    printwr.close(); 
    sendsock.close(); 
} 
+0

+1、おそらく、メッセージは送信側でフラッシュされません。 printwr.flush(); – akarnokd

+0

私はSendWriterをSendWsに保存するので、SendMsg(...)が呼び出されるたびに、それを閉じずに同じPrintWriterを何度も使用しています。フラグがtrueに設定されているため、自動的にフラッシングする必要があります。 –

+0

SendMsg()が呼び出されるたびにコードが新しいSocketとPrintWriterをインスタンス化しているように見え、どちらもメソッドの完了時に閉じられていません。 – Andrew

4

SendMsg()は新しいソケットすべての呼び出しを作成しているので、あなたは、永続的なTCP接続を使用していません。メソッドはソケットを閉じていないので、開いているコレクションがたくさんあります。プロセスが行うことができる接続の数に達している可能性があります(オブジェクトがガーベッジ・コレクションされたときにソケットが閉じられないことがあります)。最後に

、kd304は、PrintWriterコンストラクタのパラメータautoFlushについてPrintWriter状態このためJavadocを指摘したように:「trueの場合、printlnを、printf関数、またはformatメソッドは出力バッファをフラッシュします」。あなたのコードは、フラッシュしたメソッドを呼び出すことはありませんでした。

これを試してみてください:

public class MessageSender implements Closeable { 
    private final Socket socket; 
    private final PrintWriter writer; 

    public MessageSender(String dest, int port) { 
    socket = new Socket(dest, port); 
    writer = new PrintWriter(socket.getOutputStream(), true); 
    } 

    public void sendMessage(Message message) { 
    try { 
     writer.println(message.toString()); 
    } catch (UnknownHostException ex) { 
     System.out.println(ex); 
     //DO: Terminate or restart 
    } catch (IOException ex) { 
     System.out.println(ex); 
     //DO: Terminate or restart 
    } 
} 

@Override 
public void close() throws IOException { 
    writer.close(); 
    socket.close(); 
} 

sendMessage()がフォーマットされたメッセージを取得するためにMessage.toString()を呼び出すように私は、コードを変更しました。 sendMessage()がメッセージをフォーマットするためにMessageのフィールドを参照するのは正しいとは思われません。 toString()を使用する代わりに、この目的のためにMessageにメソッドを作成することができます。

は、ここで、サーバー側のコードです:

public class Server implements Runnable { 
    private final ServerSocket serverSocket; 
    private final ExecutorService executor; 
    private volatile boolean running = true; 

    public Server(int port, ExecutorService executor) throws IOException { 
    serverSocket = new ServerSocket(port); 
    this.executor = executor; 
    } 

    @Override 
    public void run() throws IOExeption { 
    while (running) { 
     Socket socket = serverSocket.accept(); 
     executor.execute(new ConnectionHandler(socket)); 
    } 
    } 

    public boolean stop(long timeout, TimeUnit unit) { 
    running = false; 
    executor.shutdown(); 
    return executor.awaitTermination(timeout, unit); 
    } 
} 

あなたがタスクを実行するためにExecutorServiceを作成するためにExecutorsを使用することができます。 ConnectionHandlerは与えられたソケットを閉じる必要があることに注意してください。

+0

Autoflushはprintln()でのみ動作しますので、常にflush()する必要があります。 – akarnokd

+0

修正済み。ありがとう!私も何かを学んだ! :-) – NamshubWriter

0

ああ、申し訳ありません。私は誤ってコードからコメントを削除しました。

public void SendMsg(String dest, Message myMsg) { 
Socket sendsock = null; 
try { 
    if(printwr == null) { 
     sendsock = new Socket(dest, Main.rcvport); 
     printwr = new PrintWriter(sendsock.getOutputStream(), true); 
    } 
    String msgtosend = myMsg.msgtype.toString() + "=" + Main.myaddy + "=" + myMsg.content + "\n"; 
    printwr.print(msgtosend); 
} catch (UnknownHostException ex) { 
    System.out.println(ex); 
    //DO: Terminate or restart 
} catch (IOException ex) { 
    System.out.println(ex); 
    //DO: Terminate or restart 
} 

}

printrwが宣言され、関数の外で保存されているので、それをセットアップしたら、sendsockまたはprintrwを再初期化する必要はありません:それはこのように、実際のです。実際のアプリケーションでは、すべての接続のPrintWriterをHashMapに保存し、SendMsg(...)関数の開始時に取得します。

接続は永続的であるため、1つが受け入れられるたびに、新しいスレッドはwhileループを実行してデータを継続的にチェックするランチです。これらのスレッドと接続は、アプリケーションが終了すると閉じられます。私の以前の質問に加えて、これを行うより効率的な方法がありますか?

私はこのコードを "\ n"を使わずにprintln(...)を使って実装しましたが、まだ受信していないメッセージの問題がありました。問題。メッセージはそのように送信されます。

public class SendPortal2 implements Runnable { 
String dest = null; 

SendPortal2 (String dest) { 
    this.dest = dest; 
} 

public void run() { 
     for(int i=1; i<1000; i+=2) { 
      Message myMsg = new Message("Message", Main.myaddy + " " + String.valueOf(i)); 
      Main.myCommMgr.SendMsg(dest, myMsg); 
     } 
} 

}

を実行している2件のそのようなスレッドがあります。もう一度コードを実行したとき、一方の側には999パケットしか得られませんでしたが、他方の側には500しかありませんでした。スレッド全体のデータがブロックされることがあります。そうだろうか?

返信いただきありがとうございます!

+0

私はサーバーサイドコードで自分の答えを更新しました。私はそれがフラッシングではないコードと、クライアントに対応するのに問題があるサーバーの組み合わせだと思います。 – NamshubWriter

0

SendMsg関数が呼び出されるforループ内にThread.sleep(2)を置くと、より多くのメッセージが正しく受信されますが、必ずしも1000ではありません。システムのリソースが2つループ中にスレッドが連続して実行されていますか?

+0

私は解決策を見つけたかもしれないと思います。 Printlnとソケットの初期化に関して皆さんからお伝えしたものに加えて、私はSendMsg関数を同期させました。それは少なくとも今のところそれを修正したようです。私は、問題の一部は、両方のスレッドがほぼ同時に起動されるため、最初のスレッドが上書きされて1つのスレッドからのカウントが不正になるように、独自のソケットを設定するということでした。スレッドにアクセスするスレッドが増えてもそれが成立するかどうかはわかりませんが、ExecutorServiceが役立つかもしれません。あなたのご親切に感謝します! – thodinc

関連する問題