0

Javaでサーバーへのソケットを作成しています。ソケットが接続された後、ソケットの入力ストリームと出力ストリームにアクセスできる新しいスレッドが作成され、このスレッドは入力行が入ってくるとブロックして処理します。

readln入力ストリームが終了すると、BufferedReaderのメソッドが null を返すことを理解しています。これは必ずしもソケットが閉じていることを意味するわけではありませんか? これは何を意味するのでしょうか?closeしたがって、ソケットでメソッドを実行して、ソケットをうまく閉じたいと思います。

readlnまた、メソッドが IOException をスローする可能性があること、およびcloseメソッドが現在ブロックされている場合、ソケットでメソッドが呼び出された後にこれがスローされることも理解しています。他にいつこれを投げることができますか?これがスローされた後もソケットがまだ開いている可能性がありますか、それとも常に閉じており、ガベージ コレクションなどの準備ができている可能性があります。

これは私が現在持っているコードであり、切断を適切に処理する方法がよくわかりません。disconnectソケットがラインを待っている間にメソッドが呼び出されると、ソケットでdisconnect呼び出すため、現時点ではデッドロックに陥る可能性があると思いcloseます。IOExceptionこれによりonがスローさreadLineれ、その catch ブロックがdisconnect再度呼び出されます。

public class SocketManager {

    private Socket socket = null;
    private PrintWriter out = null;
    private BufferedReader in = null;

    private String ip;
    private int port;

    private Object socketLock = new Object();

    public SocketManager(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public void connect() throws UnableToConnectException, AlreadyConnectedException {
        synchronized(socketLock) {
            if (socket == null || socket.isClosed()) {
                throw (new AlreadyConnectedException());
            }
            try {
                socket = new Socket(ip, port);
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                throw (new UnableToConnectException());
            }
            new Thread(new SocketThread()).start();
        }
    }

    public void disconnect() throws NotConnectedException {
        synchronized(socketLock) {
            if (isConnected()) {
                throw (new NotConnectedException());
            }
            try {
                socket.close();
            } catch (IOException e) {}
        }
    }

    public boolean isConnected() {
        synchronized(socketLock) {
            return (socket != null && !socket.isClosed());
        }
    }

    private class SocketThread implements Runnable {

        @Override
        public void run() {
            String inputLine = null;
            try {
                while((inputLine = in.readLine()) != null) {
                    // do stuff
                }
                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e) {}
                }
            } catch (IOException e) {
                // try and disconnect (if not already disconnected) and end thread
                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e1) {}
                }
            }
        }

    }
}

基本的に、次のことを達成するための最良の方法を知りたいです。

  • ソケットに接続し、入力をリッスンする別のスレッドを開始する接続メソッドを作成します。
  • ソケットから切断し、入力をリッスンしているスレッドを終了する切断メソッドを作成します。
  • リモート ソケットへの接続が切断されたシナリオの処理。

私はソケットに関するJavaチュートリアルを読みましたが、私の意見では、これらについてはあまり詳しく説明していません。

ありがとう!

4

3 に答える 3

2

デッドロックになる可能性があると言ったとき、私は間違っていたと思います。

何が起こるかは次のとおりです。

  1. in.readLine()ブロック中に呼び出された disconnect()
  2. socket.close() が実行されました。
  3. in.readline() は IOException をスローします。

次に、SocketThread の例外ハンドラーが disconnect を呼び出し、disconnect がその例外が終了するのを待っていると考えていました。どちらも異なるスレッドであるため、例外が SocketThread でキャッチされている間、disconnect() のコードが続行されるため、それは問題ではありません。その後、SocketThread は disconnect() を呼び出しますが、disconnect() の最初のインスタンスが終了するまで待機する必要があります。次に、connection() が再度実行されますが、NotConnectedException がスローされ、SocketThread でキャッチされて何も起こりません。SocketThread は終了し、それが望ましい結果です。

ただし、ソケットクラスを調べたところ、次のメソッドも含まれています。

  • shutdownInput()
  • shutdownOutput()

shutdownInput()終了 EOF シンボルを入力ストリームに送信します。これin.readline()は、null を返し、ループが正常に終了することを意味します。shutdownOutput()切断していることをサーバーに通知する TCP 終了シーケンスを送信します。

socket.close()より多くのオーバーヘッドを持つ例外がスローされた結果としてスレッドが終了するのではなく、スレッドが適切に終了することを意味するため、これらの両方を前に呼び出すことはより理にかなっています。

したがって、これは変更されたコードです。

public class SocketManager {

    private Socket socket = null;
    private PrintWriter out = null;
    private BufferedReader in = null;

    private String ip;
    private int port;

    private Object socketLock = new Object();

    public SocketManager(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public void connect() throws UnableToConnectException, AlreadyConnectedException {
        synchronized(socketLock) {
            if (socket == null || socket.isClosed()) {
                throw (new AlreadyConnectedException());
            }
            try {
                socket = new Socket(ip, port);
                out = new PrintWriter(socket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                throw (new UnableToConnectException());
            }
            new Thread(new SocketThread()).start();
        }
    }

    public void disconnect() throws NotConnectedException {
        synchronized(socketLock) {
            if (isConnected()) {
                throw (new NotConnectedException());
            }
            try {
                socket.shutdownInput();
            } catch (IOException e) {}
            try {
                socket.shutdownOutput();
            } catch (IOException e) {}
            try {
                socket.close();
            } catch (IOException e) {}
        }
    }

    public boolean isConnected() {
        synchronized(socketLock) {
            return (socket != null && !socket.isClosed());
        }
    }

    private class SocketThread implements Runnable {

        @Override
        public void run() {
            String inputLine = null;
            try {
                while((inputLine = in.readLine()) != null) {
                    // do stuff (probably in another thread)
                }

                // it will get here if socket.shutdownInput() has been called (in disconnect)
                // or possibly when the server disconnects the clients


                // if it is here as a result of socket.shutdownInput() in disconnect()
                // then isConnected() will block until disconnect() finishes.
                // then isConnected() will return false and the thread will terminate.

                // if it ended up here because the server disconnected the client then
                // isConnected() won't block and return true meaning that disconnect()
                // will be called and the socket will be completely closed

                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e) {}
                }
            } catch (IOException e) {
                // try and disconnect (if not already disconnected) and end thread
                if (isConnected()) {
                    try {
                        disconnect();
                    } catch (NotConnectedException e1) {}
                }
            }
        }

    }
}
于 2013-07-21T21:15:51.370 に答える
0

あなたは正しい軌道に乗っています。「readline」は使用せず、生の読み取りのみを使用し、「do stuff」は受信データのキューの構築に限定する必要があります。同様に、返信の書き込みは、送信するデータのキューを空にする別のスレッドである必要があります。

ソケットの完全性の保証にもかかわらず、問題が発生し、意味をなさないデータを受け取ることがあります。「読み取り」と「書き込み」の下にはがらくたがあり、完璧なシステムやバグのないシステムはありません。自分の読み書きのレベルでチェックサムを含む独自のラッパーを追加して、意図したものを確実に受信できるようにします。

于 2013-07-21T18:55:45.977 に答える
0

ソケットに関連付けられたすべてのリソースが確実に解放されるようにするには、そのソケットでの作業が終了したときに close() メソッドを呼び出す必要があります。典型的な IO 例外処理パターンは、それをキャッチし、close() メソッドを呼び出してすべてをクリーンアップするために最善を尽くすことです。

したがって、必要な唯一のことは、ソケットの存続期間中にすべてのソケットで close() を呼び出すことを確認することです。

于 2013-07-21T18:50:17.353 に答える