【问题标题】:ServerSocket wont wait until it's closed to reopenServerSocket 不会等到它关闭再重新打开
【发布时间】:2016-07-26 15:33:09
【问题描述】:

我正在用 java 编写客户端/服务器应用程序,并且我正在尝试使服务器能够自行重新启动。目前,服务器接受一个连接,从客户端读取一个对象,然后将对象/套接字/流传递给一个新的线程,该线程从那里接管。下一部分是问题所在:要重新启动已经运行的服务器(让我们将此实例称为“实例 1”),我启动它的一个新实例(实例 2)。如果 ServerSocket 已经在使用,实例 2 应该向 ServerSocket 写入一个空值,这将导致实例 1 关闭它,然后实例 2 应该像以前一样运行相同的块。这样做会成功终止实例 1,但除非我进入调试模式并控制何时发生哪一行,否则实例 2 会在 ServerSocket 关闭之前尝试重新打开它,因此它也会终止。我无法让实例 2 等到 ServerSocket 关闭后再尝试打开它。有没有我找不到的方法或实现?

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Tester {

    protected ExecutorService threadPool = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        new Tester();
    }

    public Tester () {
        try {
            listen();
        } catch (Exception e) {
            try {
                Socket socket = new Socket("127.0.0.1", 4444);
                new ObjectOutputStream(socket.getOutputStream()).writeObject(null);
                socket.close();

                listen();
            } catch (Exception ex) { e.printStackTrace(); }
        }

        return;
    }

    public void listen () throws Exception {
        try (ServerSocket serverSocket = new ServerSocket(4444);) {
            while (true) {
                Socket clientSocket = serverSocket.accept();
                ObjectInputStream in = new ObjectInputStream(clientSocket.getInputStream());
                Object obj = in.readObject();
                if(obj == null)
                    break;
          //    this.threadPool.execute(new ServerThread( ... ));
            }
            threadPool.shutdown();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception();
        }
    }
}

【问题讨论】:

  • 你需要学习多线程的概念,它有助于实现你想要的。此外,您想要实现的目标也不清楚。
  • 没错,但我很确定我至少知道这部分。我的问题是我找不到任何 ServerSockets 实现,它可以让我在实例 2 打开之前安全地等待实例 1 关闭 ServerSocket。我想要实现的是能够只打开服务器的另一个实例(此处为测试器),它将关闭前一个实例,然后像旧实例一样接管端口。最终我将添加实现以优雅地关闭 ServerSocket,这样我们就不需要在每次想要重新启动服务器时断开所有客户端。
  • 那你将如何更新你的服务器?我们需要能够在不杀死前一个线程的情况下即时更新服务器。线程可能需要几个小时,所以我们不能只告诉客户端上的用户停止使用服务器,直到线程完成。该解决方案将让服务器的实例 1 完成其旧线程,并让新的和改进的实例 2 处理新的

标签: java sockets serversocket


【解决方案1】:

尝试在延时循环中打开 ServerSocket,比如

for (int i = 0; i < 3; i++) {
  Thread.sleep(2000); //select your sleep time
  try {
    reopenMyServerSocket();
    break;
  }
  catch (IOException iox) {}
}

【讨论】:

  • 这允许它工作,但这只是因为你已经欺骗它不再是一个真正的竞争条件,因为实例 1 应该花费不到 6 秒的时间来关闭套接字,对吗?实际上,这个程序将在一个非常慢的服务器上运行,所以我对第一个实例应该击败的硬编码延迟犹豫不决。似乎我应该能够用一个while循环交换for循环,用一些标志交换“break”以退出while循环。这将永远循环,直到套接字再次打开,对吧?
【解决方案2】:

使用线程将是处理此问题的合适方法。如果您想使用线程,那么您可以按照我的示例进行操作。

    public static void main(String[] args) {            
        renderServer(9000);//render socket and handle request
    }

    public static void renderServer(int port){    
        ServerSocket  serverSocket  = new ServerSocket (port);
        System.out.println("Server started on "+ new Date());     

        while(true){
            Socket socket = serverSocket.accept(); 

            HandleClient handleClient = new HandleClient(socket);
            Thread thread = new Thread(handleClient);
            thread.start();
        }
    }

然后您必须创建处理每个请求的 HandleClient 类。请注意,HandleClient 类必须实现 Runnable,如下例所示。

public class HandleClient implements Runnable{ 

        Socket socket;

        public HandleClient(Socket socket){
            this.socket = socket;
        }


        @Override
        public void run() {
            //Your code goes in here
            DataInputStream in = new DataInputStream(socket.getInputStream());
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
            //Then you can continue from here
        }

}

【讨论】:

  • 谢谢,但实际上我已经有一个单独的 Thread 类来执行此操作。不过效果很好,谢谢你的建议!你是绝对正确的。但是,我在处理不同的部分时遇到了麻烦。我正在尝试设置我的服务器以重新启动其先前的实例。因此,当它启动并看到其端口已被使用(由第一个实例)时,它会向其发送一个信号,第一个实例将其解释为关闭信号,然后新实例应接管该端口。跨度>
  • 好的,现在知道了。但这将是一件困难的事情。该线程可以处理同一端口上的多个实例。你也知道这一点。我不知道你想做什么但是............
  • 酷,谢谢!但我实际上认为我坚持使用 ControlAltDel 的解决方案,只是在他的 cmets 中添加了一些小调整。尽管如此,还是感谢您的帮助!这是一个非常非常好的答案,只是针对一个稍微不同的问题。顺便说一句,问题是服务器的新实例需要在与前一个实例相同的端口上打开,但我们不能在一个端口上打开两个 ServerSocket。所以我们需要关闭旧版本的,但我们不会丢失所有当前客户端的连接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多