Try-with-resources 关闭生成的孩子的套接字

Posted

技术标签:

【中文标题】Try-with-resources 关闭生成的孩子的套接字【英文标题】:Try-with-resources closes sockets of spawned childs 【发布时间】:2014-12-01 15:02:13 【问题描述】:

我想编写一个简单的服务器来侦听端口并生成新线程来处理新连接。 我尝试使用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) 中的示例接受连接但立即关闭它。 有人可以向我解释原因并告诉我如何正确执行此操作吗?

【问题讨论】:

这是意料之中的,因为这是 try-with-resources 的工作方式……在 try 块之后,在开头括号中声明的所有资源都将关闭。 感谢您的回答。那么你会说在这里使用 try-with-resources 是错误的方法吗?你能推荐一个更优雅的版本吗? 好吧,只是传递套接字,并在线程中使用 try-with-resources 语句使其关闭(您可以final Socket socket = s; s 已经存在) 另外,您应该考虑使用 ExecutorService 而不是原始线程 【参考方案1】:

结构

try (resource = ...) 
 

等价于

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

就是这样。它只是一个语法糖,只是写相同的更短的方法。因此,当您将语句 Socket clientSocket = serverSocket.accept(); 放入 try-with-resource 块时,您实际上会在离开块后关闭它。

当流的处理同步完成时,即当您打开流,读取或写入并关闭它时,这种结构很好。

在您的情况下,您获取流并在单独的线程中处理它,因此无法立即关闭它。客户端应决定关闭流本身。例如,当用户按下按钮“断开连接”或服务器发送特殊应用程序级别命令“关闭连接”或抛出IOException 时。

【讨论】:

不,不等价。 try-with-resources 块中的资源在catch 块(如果有)之前 关闭。 感谢 AlexR 和 fge。

以上是关于Try-with-resources 关闭生成的孩子的套接字的主要内容,如果未能解决你的问题,请参考以下文章

格式化 try-with-resources 的代码约定是啥? [关闭]

try-with-resources 关闭异常中的流控制

你是否还在写try-catch-finally?来使用try-with-resources优雅地关闭

使用 try-with-resources 优雅关闭资源

使用 try-with-resources java 关闭数据库连接

如何修复“SQLServerException:连接已关闭”。在这个 Try-With-Resources 块中出现?