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-catch-finally?来使用try-with-resources优雅地关闭