1

ポートをリッスンし、新しい接続を処理するために新しいスレッドを生成する単純なサーバーを作成したいと考えています。新しい接続を受け入れるために try-with-resourcesを使用しようとしましたが、子スレッドのソケットがすぐに閉じられたように見え、その理由がわかりません。

簡単な例を 2 つ示します。
a) サーバーの実例 (try-with-resources なし):

package MyTest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerA implements Runnable  {
    private int port;
    private ServerSocket serverSocket;

    public MyServerA(Integer port)  {
        this.port = port;
    }

    @Override
    public void run() {
        try     {   
            serverSocket = new ServerSocket(port);
        } catch(IOException ioe) {
            System.err.println("error opening socket. " + ioe.getStackTrace());
        }

        while (true) {
            Socket clientSocket = null;
            try {
                clientSocket = serverSocket.accept();
                ClientServiceThread cliThread = new ClientServiceThread(clientSocket);
                cliThread.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ClientServiceThread extends Thread {
        private Socket s;
        boolean goOn = true;

        ClientServiceThread(Socket s) {
            this.s = s;
        }

        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;

            try {
                in = new BufferedReader(new InputStreamReader(this.s.getInputStream()));
                out = new PrintWriter(new OutputStreamWriter(this.s.getOutputStream()));

                while (goOn) {
                    final String req = in.readLine();
                    if (req != null) {
                        System.out.println("got: " + req);
                        out.println("you said: " + req);
                        out.flush();

                        if (req.contains("bye")) {
                            System.out.println("closing thread");
                            goOn = false;
                        }
                    }
                }
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyServerA a = new MyServerA(30000);
        a.run();
    }
}

b) まったく同じですが、try-with-resources を使用します (機能しません):

package MyTest;

import java.io.BufferedReader;

public class MyServerB implements Runnable  {
    private int port;
    private ServerSocket serverSocket;

    public MyServerB(Integer port)  {
        this.port = port;
    }

    @Override
    public void run() {
        try {   
            serverSocket = new ServerSocket(port);
        } catch(IOException ioe) {
            System.err.println("error opening socket. " + ioe.getStackTrace());
        }

        while (true) {
            try (Socket clientSocket = serverSocket.accept();) {
                ClientServiceThread cliThread = new ClientServiceThread(clientSocket);
                cliThread.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class ClientServiceThread extends Thread {
        private Socket s;
        boolean goOn = true;

        ClientServiceThread(Socket s) {
            this.s = s;
        }

        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;

            try {
                in = new BufferedReader(new InputStreamReader(this.s.getInputStream()));
                out = new PrintWriter(new OutputStreamWriter(this.s.getOutputStream()));

                while (goOn) {
                    final String req = in.readLine();
                    if (req != null) {
                        System.out.println("got: " + req);
                        out.println("you said: " + req);
                        out.flush();

                        if (req.contains("bye")) {
                            System.out.println("closing thread");
                            goOn = false;
                        }
                    }
                }
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyServerB b = new MyServerB(30000);
        b.run();
    }
}

a) の例は期待どおりに動作します。b) の例では、接続を受け入れますが、すぐに閉じます。誰かが理由を説明し、これを適切に行う方法を教えてもらえますか?

4

1 に答える 1

6

構造

try (resource = ...) {
} 

と同等です

resource = null;
try {
   resource = ...;
}  finally {
    if (resource != null) {
        resource.close();
    }
}

それでおしまい。これは単なるシンタックス シュガーであり、同じものをより短く書く方法です。したがって、ステートメントSocket clientSocket = serverSocket.accept();を try-with-resource ブロックに入れると、ブロックを離れると実際にそれを閉じます。

この構造は、ストリームの処理が同期的に行われる場合、つまり、ストリームを開いて読み取りまたは書き込みを行い、それを閉じる場合に適しています。

あなたの場合、ストリームを取得して別のスレッドで処理するため、すぐに閉じることはできません。クライアントは、ストリーム自体を閉じることを決定する必要があります。たとえば、ユーザーがボタン「切断」を押したとき、またはサーバーが特別なアプリケーション レベル コマンド「接続を閉じる」を送信したとき、またはIOExceptionスローされた場合などです。

于 2014-12-01T15:13:16.907 に答える