如何正确关闭一个socket?

22
这是一个简单的TCP服务器。当程序终止时,我该如何关闭套接字?我尝试使用try/finally并尝试关闭套接字。但是当我退出程序时,它不会运行finally块。有人有关闭套接字的正确方法吗?
try {
        socket = new ServerSocket(port);
        System.out.println("Server is starting on port " + port + " ...");
    }catch (IOException e){
        System.out.println("Error on socket creation!");
    }

    Socket connectionSocket = null;
    try{
        while(true){            
            try{
                connectionSocket = socket.accept();
                Thread t =  new Thread(new ClientConnection(connectionSocket));
                t.start();
            }catch (IOException e) {
                System.out.println("Error on accept socket!");
            }
        }
    }finally{
        this.socket.close();
        System.out.println("The server is shut down!");
    }

1
在finally块中调用套接字的close()方法,或者现在使用Java 7的try-with-resources块,它会自动为您关闭它们。http://www.javacodegeeks.com/2011/07/java-7-try-with-resources-explained.html - Hunter McMillen
1
在调用close()之前,你还需要进行空值检查,以防ServerSocket构造函数抛出异常。 - unholysampler
@Kit Ho,应用程序如何终止? - mre
1
@mre:只需在Eclipse中按下终止按钮或在控制台中按下Control-C即可。我想添加一个最终消息,说“服务器已关闭”,在Python中,这很容易做到,但不知道在Java中怎么做。 - TheOneTeam
6
建议您将 IOException 的捕获放在循环外部。由于这种错误几乎不可能恢复,因此无限循环可能不是一个好主意。 - Peter Lawrey
2
@Peter Lawrey +1,否则在catch块中添加break - user207421
5个回答

18
创建了ServerSocket之后,您可以添加一个ShutdownHook以在JVM终止时关闭它,例如:
Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
    try {
        socket.close();
        System.out.println("The server is shut down!");
    } catch (IOException e) { /* failed */ }
}});

调用ServerSocket#close将终止阻塞的ServerSocket.accept调用,导致它抛出SocketException。但是,请注意,您在while循环中对IOException的当前处理意味着您将重新进入while循环以尝试接受已关闭的套接字。JVM仍将终止,但有点不整洁。

如果您在Eclipse中终止控制台应用程序(至少在Windows上),则关闭挂钩不会运行。但是,如果您在常规控制台中CTRL-C Java,则它们会运行。为了使它们运行,您需要正常终止JVM,例如SIGINT或SIGTERM,而不是SIGKILL(kill -9)。

您可以在Eclipse或控制台中执行的简单程序将证明此事。

public class Test implements Runnable {

  public static void main(String[] args) throws InterruptedException {
    final Test test = new Test();
    Runtime.getRuntime().addShutdownHook(new Thread(){public void run(){
      test.shutdown();
    }});
    Thread t = new Thread(test);
    t.start();
  }

  public void run() {
    synchronized(this) {
      try {
        System.err.println("running");
        wait();
      } catch (InterruptedException e) {}
    }
  }

  public void shutdown() {
    System.err.println("shutdown");
  }
}

1
ShutDownHookжҖ»жҳҜиў«жү§иЎҢеҗ—пјҹдҫқиө–е®ғ们жҳҜдёҖз§Қеёёи§Ғдё”е®үе…Ёзҡ„еҒҡжі•еҗ—пјҹ - Marsellus Wallace
2
正如javadoc所说,ShutdownHooks在JVM正常终止时被运行。如果你使用"kill -9"命令杀死Java进程,它们将不会被执行。我曾经看到过商业企业软件中可靠地使用shutdown hooks来有序关闭服务器进程。 - ewan.chalmers
1
@sudocode 关闭挂钩不是解决此问题的一部分。他在终止时不需要关闭套接字。操作系统会为他执行此操作。他的问题是 finally }{} 块未被执行,因此他从未看到自己的消息,所以他认为他的套接字没有关闭。 - user207421
@EJP在他的评论中表示,他想在按下“Ctrl+C”时打印一条消息,这种情况下使用ShutdownHook解决了他的一个问题(虽然不是主要问题)。 - Matthieu
关闭挂钩只有在程序正常终止时才能起作用,但如果我从任务管理器中杀死程序...它将无法工作。 - Zuko
显示剩余2条评论

8

在您的情况下不需要,当程序退出时,操作系统会自动关闭所有TCP套接字。


7
您说得对,操作系统将在程序退出时关闭所有TCP套接字,但我不会在所有情况下说“不需要”。这取决于您是否需要在退出此程序之前释放监听的端口(虽然在这种情况下,应该没问题,因为OP想要在程序退出时关闭它)。 - xav

2

来自 javadoc :

Java运行时会自动关闭输入和输出流、客户端套接字和服务器套接字,因为它们是在try-with-resources语句中创建的。

此外

The finalize() method is called by the Java virtual machine (JVM) before the program exits to give the program a chance to clean up and release resources. Multi-threaded programs should close all Files and Sockets they use before exiting so they do not face resource starvation. The call to server.close() in the finalize() method closes the Socket connection used by each thread in this program.

protected void finalize(){
//Objects created in run method are finalized when
//program terminates and thread exits
     try{
        server.close();
    } catch (IOException e) {
        System.out.println("Could not close socket");
        System.exit(-1);
    }
  }

1

为什么 finally 没有运行?可能 while(true) 应该被替换成类似于

while (!shutdownRequested)

或者你可以创建一个关闭挂钩来处理套接字的关闭


一些挑剔的提示,请查看 Javadoc。你想要关闭,而不是销毁。http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html - Daniel B. Chapman
没听懂,你能详细解释一下吗? - TheOneTeam
你可以尝试一下,但是消息“服务器已关闭”不会被打印! - TheOneTeam

0

那么,如何“退出”程序?finally语句块将被执行,无论是否抛出异常或是try块以“正常”的方式结束,但我认为这可能会因为你的while(true)而变得“困难”。

要关闭套接字,您应该使用socket.close(),我建议您不要依赖destroy函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接