2016-06-18 4 views
1

私は本当にTCPソケット通信の背後にある理論の多くを知りませんが、実際の外に、私は次のコードを達成した:1つのクライアントが接続を失った後にTCPサーバーソケットが閉じられるのはなぜですか?

サーバー:

public class Server { 

    public static volatile ArrayList<ReplyThread> connections = new ArrayList<>(); 

    public static void main(String[] args) { 
     new AcceptThread().start(); 
    } 

    private static class AcceptThread extends Thread { 
     @Override 
     public void run() { 
      ServerSocket inSock; 

      try { 
       inSock = new ServerSocket(3074); 

       boolean loop = true; 
       while(loop) { 
        System.out.println("waiting for next connection"); 
        connections.add(new ReplyThread(inSock.accept())); 
        System.out.println("connection made"); 
        connections.get(connections.size() - 1).setName(""+(connections.size() - 1)); 
        connections.get(connections.size() - 1).start(); 
       } 
       inSock.close(); 

      } catch (IOException ex) { 
       System.out.println(ex.getMessage()); 
      } 
     } 
    } 

    public static class ReplyThread extends Thread { 
     private static Socket sock; 
     private DataOutputStream out; 

     public ReplyThread(Socket newSock) { 
      sock = newSock; 
     } 

     @Override 
     public void run() { 
      try { 
       DataInputStream in = new DataInputStream(sock.getInputStream()); 
       out = new DataOutputStream(sock.getOutputStream()); 

       boolean loop = true; 
       while(loop) { 
        String msg = in.readUTF(); 
        System.out.println(msg); 
        for (ReplyThread thread : connections) { 
         thread.output(sock, msg); 
        } 
       } 

       in.close(); 
      } catch (SocketException ex) { 
       Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
       System.out.println("Connection terminated."); 
       this.interrupt(); 
       System.out.println(this.getName() + " I was interrupted"); 
      } catch (IOException ex) { 
       Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 

     public final void output(Socket sock, String message) throws IOException { 
      this.out.writeUTF(this.getName() + ": " + message); 
     } 
    } 
} 

クライアント:

package server; 

import java.io.BufferedReader; 
import java.io.DataInputStream; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.net.Socket; 
import java.net.URL; 
import java.util.Scanner; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
/* 
* @author RaKXeR 
*/ 
public class Client { 

    public static Socket sock; 

    public static void main(String[] args) { 
     try { 
      sock = new Socket("localhost", 3074); 
      new writeThread().start(); 
      new readThread().start(); 

     } catch (IOException ex) { 
      Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    public static class readThread extends Thread { 
     @Override 
     public void run() { 
      boolean loop = true; 
      while (loop) { 
       try { 

        DataInputStream in = new DataInputStream(sock.getInputStream()); 
        //BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream())); 
        String msg = in.readUTF(); 
        //String msg = in.readLine(); 
        System.out.println(msg); 

       } catch (IOException ex) { 
        System.out.println("read error"); 
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
        loop = false; 
       } 
      } 
     } 
    } 

    public static class writeThread extends Thread { 
     @Override 
     public void run() { 
      boolean loop = true; 
      while(loop) { 
       try { 
        DataOutputStream out = new DataOutputStream(sock.getOutputStream()); 
        System.out.println("Type your message to the server: "); 
        Scanner scan = new Scanner(System.in); 
        out.writeUTF(scan.nextLine()); 

       } catch (IOException ex) { 
        System.out.println("write error"); 
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
        loop = false; 
       } 
      } 
     } 
    } 

} 

このプログラムはあまり効果がありません。サーバーを開き、サーバーソケット上の着信接続を待っているスレッドを開始します。サーバーソケットに接続しようとするクライアントを開くと、接続を受け入れ、そのスレッド配列リストに新しいスレッドを追加し、そのクライアント/クライアントソケットからのメッセージの待機を開始します。

新しいクライアントが接続しようとするたびに、私は彼を別のスレッドに追加して、すぐに両方のクライアントと対話することができます。

1つのクライアントがサーバーにメッセージを送信するとすぐに、サーバーは接続されているすべてのクライアントに同じメッセージをスレッドの番号と共に送信します。そして、これらのすべてが意図どおりに機能します。

しかし、私の問題は、クライアントの1つを停止するとすべてが機能しなくなることです。私が私にこのxDを求めるとは思わないと知っていたから正確に私が意味することを説明するのではなく、あなた自身でコードをテストするほうがよいでしょう。

実際の質問:これは何が原因で、私のコードに関するすべてを変更することなくこれを修正できますか?私は、サーバソケットをいくつかのクライアントソケットを作成するために再利用していることに気づいていますが、わかりません。

EDIT:あなたは、ジム・ギャリソンの答えの下に見ることができる、と彼は、問題が何であったかを説明 - 私はオフラインたクライアントに他のクライアントのメッセージを送信しようとしていた、それが例外をスローし、停止します糸。私が修正したのは、閉じられたスレッドの名前に "Terminated"の "T"を追加して、スレッドのすべての名前をチェックしてから情報を送信することでした。それは完璧ではありませんが、私は今すぐ解決しています。このスレッドのコードをベースとして使用したことがある場合は、このコードが優れたxDではないため、ごめんなさい。とにかく、元のプログラムのように、改善することをお勧めします。ここでは、固定サーバーコードは次のとおりです。1つのクライアントが終了

package server; 

import java.io.DataInputStream; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.net.SocketException; 
import java.util.ArrayList; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class Server { 

    public static volatile ArrayList<ReplyThread> connections = new ArrayList<>(); 

    public static void main(String[] args) { 
     new AcceptThread().start(); 
    } 

    private static class AcceptThread extends Thread { 
     @Override 
     public void run() { 
      ServerSocket inSock; 

      try { 
       inSock = new ServerSocket(3074); 

       boolean loop = true; 
       while(loop) { 
        System.out.println("waiting for next connection"); 
        connections.add(new ReplyThread(inSock.accept())); 
        System.out.println("connection made"); 
        connections.get(connections.size() - 1).setName(""+(connections.size() - 1)); 
        connections.get(connections.size() - 1).start(); 
       } 
       inSock.close(); 

      } catch (IOException ex) { 
       System.out.println(ex.getMessage()); 
      } 
     } 
    } 

    public static class ReplyThread extends Thread { 
     private static Socket sock; 
     private DataOutputStream out; 

     public ReplyThread(Socket newSock) { 
      sock = newSock; 
     } 

     @Override 
     public void run() { 
      try { 
       DataInputStream in = new DataInputStream(sock.getInputStream()); 
       out = new DataOutputStream(sock.getOutputStream()); 

       boolean loop = true; 
       while(loop) { 
        String msg = in.readUTF(); 
        System.out.println(msg); 
        for (ReplyThread thread : connections) { 
         if (!thread.getName().contains("T")) thread.output(sock, msg); 
        } 
       } 

       in.close(); 
      } catch (SocketException ex) { 
       //Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
       System.out.println("Connection terminated."); 
       this.setName(this.getName() + "T"); 
       this.interrupt(); 
       System.out.println(this.getName() + " I was interrupted"); 
      } catch (IOException ex) { 
       Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     } 

     public final void output(Socket sock, String message) throws IOException { 
      this.out.writeUTF(this.getName() + ": " + message); 
     } 
    } 
} 
+0

クラッシュ後にサーバーログを追加できますか? – pedrofb

+0

どのようにクライアントの実行を停止しますか?_どちらの側でも正常に接続をシャットダウンするコードはありません。 –

+0

ここで大きな同時実行性の問題があります。 'Socket'メンバは静的であるクラスでは静的であってはいけません。 – EJP

答えて

2

した後、対応するReplyThreadSocketExceptionを取得し、終了します。ただし、接続配列リストはクリーンアップしないでください。まだ接続されている各クライアントがメッセージを送信すると、今閉じているクライアントに返信を試みます。これにより、現在の送信側クライアントにはReplyThreadが終了する例外がスローされます。

つまり、1つのクライアントが終了すると、残りのクライアントごとにReplyThreadがそのクライアントに対してメッセージを受信すると消滅します。

解決方法は、接続終端を処理するコードを追加し、接続がまだアクティブであるサーバーのビューの正確性と一貫性を確保することです。

+0

ありがとう、これに基づいて私はほぼ即座に解決策を作成しました。私はあなたの答えを正しいとマークし、自分の解決策を含めるように質問を編集します – RaKXeR

1

クライアントが接続を失った後にサーバーソケットが閉じない。問題は閉じたソケットにデータを書き込んで例外を生成するという意味のReplyThreadです。可能な解決策は次のとおりです。ArrayList以来

while(loop) { 
    String msg = in.readUTF(); 
    System.out.println(msg); 
    /*for (ReplyThread thread : connections) { 
     thread.output(sock, msg); 
    }*/ 
    synchronized (connections) { 
     for (Iterator iterator = connections.iterator(); iterator.hasNext();) { 
      ReplyThread thread = (ReplyThread) iterator.next(); 
      if (thread.sock.isClosed()) { 
       iterator.remove(); 
      } else { 
       try { 
        thread.output(thread.sock, msg); 
       } catch (IOException e0) { 
        iterator.remove(); 
       } 
      } 
     } 
    } 
} 

はスレッドセーフではない、私はサンプルコード内のリソースのロックを同期さを使用しています。 connectionsのもう1つのメソッド呼び出しでも同じ修正が適用されます。

関連する問題