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 回答