【问题标题】:How can I close the socket in a proper way? [duplicate]如何以正确的方式关闭套接字? [复制]
【发布时间】:2011-12-24 12:15:09
【问题描述】:

这是一个简单的 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!");
    }

【问题讨论】:

  • 在您的 finally 块中调用套接字 close() 方法,或者现在使用 java 7 您可以使用 try-with-resources 块,它会为您关闭它们。 javacodegeeks.com/2011/07/…
  • 您还需要在调用close() 之前进行空检查,以防ServerSocket 构造函数抛出异常。
  • @mre:只需按 Eclipse 中的终止按钮或控制台中的 control-c 即可。我想添加一个最后的消息说“服务器已关闭”,在 python 中,这很容易做到,但在 java 中不知道
  • 我会在你的循环之外捕获 IOException。您不太可能从该错误中恢复,因此无限循环可能不是一个好主意。
  • @Peter Lawrey +1,或者在 catch 块中添加 break

标签: java sockets tcp


【解决方案1】:

创建 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");
  }
}

【讨论】:

  • ShutDownHooks 总是被执行吗?依赖这些是一种常见且安全的做法吗?
  • @Gevorg 正如javadoc所说,ShutdownHooks由JVM运行,然后在正常终止时退出。如果你“杀死 -9”一个 Java 进程,它们不会。我已经看到可靠地使用关闭挂钩来有序关闭商业企业软件中的服务器进程。
  • 他的问题是跳出accept() 循环进入finally{} 块,而不是一旦他到达那里该做什么。
  • @EJP 部分正确 - ShutdownHook 提供了一种在 JVM 终止时中断 socket.accept() 的方法,但当前在 while 循环中对 IOException(以及扩展的 SocketException)的处理破坏了这一点。但是,我的回答根本没有解决 finally 块。 OP 错误地认为 finally 应该在 JVM 终止时运行。
  • @sudocode 关闭挂钩不是此问题解决方案的一部分。他不需要在终止时关闭套接字。操作系统将为他做到这一点。他的问题是 finally }{} 块没有被执行,所以他永远看不到他的消息,所以他认为他的套接字没有关闭。
【解决方案2】:

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

【讨论】:

  • 你说得对,当程序退出时操作系统会关闭所有的 TCP 套接字,但我不会在所有情况下都说 “不需要”。这取决于您是否需要在退出此程序之前释放侦听的端口(尽管在这种情况下,应该没问题,因为 OP 希望在程序退出时关闭它)
【解决方案3】:

来自javadoc

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

还有

finalize()方法被Java虚拟机调用(JVM) 在程序退出之前给程序一个清理的机会 释放资源。多线程程序应关闭所有文件和 他们在退出之前使用的套接字,因此他们不会面对资源 饥饿。 finalize() 方法中对 server.close() 的调用关闭 该程序中每个线程使用的 Socket 连接。

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);
    }
  }

【讨论】:

    【解决方案4】:

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

    while (!shutdownRequested)
    

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

    【讨论】:

    【解决方案5】:

    那么,你如何“退出”程序? finally 将在抛出异常或 try 块以“正常”方式完成执行时执行,但我认为由于您的 while(true),这可能是“困难”的。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      • 2012-02-10
      • 2018-07-27
      • 2020-03-20
      • 1970-01-01
      • 2016-05-25
      • 1970-01-01
      相关资源
      最近更新 更多