【问题标题】:Multi thread socket server shutdown by message Java通过消息 Java 关闭多线程套接字服务器
【发布时间】:2019-12-31 08:19:02
【问题描述】:

我有一个多线程进程来监听消息并处理它们。如果收到的消息之一是“关闭”,我希望能够关闭进程。我已经实现了除关机部分之外的所有内容。

我有一个使用start方法扩展java.net.ServerSocket 的“Multi”类。里面……

java.net.Socket socket = null;
while (true) {
            try {
                socket = this.accept();
                new Thread(new SocketThread(socket, verifier, threading)).start();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

那个 SocketThread 是 implements Runnable 的另一个类。有什么办法可以使这项工作?

【问题讨论】:

  • 如果你使用的是spring boot(推荐),你可以使用内置的/actuator/shutdown端点
  • 您应该保留一个正在运行的线程列表,一旦关闭请求到达,就中断线程(或等到它们完成)。使用标志来指示进程是否正在运行并且可以启动新线程,或者收到关闭请求并且不能再为请求提供服务。
  • 没有。您只需要在收到此消息时关闭ServerSocket。您应该让所有当前正在运行的处理程序运行到完成或读取超时。强制关闭它们是残酷和毫无意义的。 @PavelSmirnov
  • @user207421,我没有说“强制关闭”,我说的是“中断”。
  • 您要关闭哪个进程? while (true) 还是 SocketThread?

标签: java multithreading maven sockets runnable


【解决方案1】:

我以前不必这样做(因为我通常发现自己不必编写太多原始 SocketServer),但是通过这种类型的线程协调,我会尝试以下类似的操作...

public class App {
    public static void main(String[] args) {
        new App().run();
    }

    public void run() {
        try {
            System.out.println("Starting...");
            AtomicBoolean running = new AtomicBoolean(true);
            Collection<Socket> sockets = new ArrayList<>();
            Collection<Thread> threads = new ArrayList<>();
            try (ServerSocket socketServer = new ServerSocket(10101)) {
                System.out.println("Started.");
                while (running.get()) {
                    Socket socket = socketServer.accept();
                    sockets.add(socket);
                    if (running.get()) {
                        Thread thread = new Thread(new SocketHandler(socket, running));
                        thread.start();
                        threads.add(thread);
                    }
                }
                System.out.println("Stopping...");
                sockets.forEach(socket -> {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
                threads.forEach(thread -> {
                    try {
                        thread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
            System.out.println("Stopped.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class SocketHandler implements Runnable {
        private final Socket socket;
        private final AtomicBoolean running;

        SocketHandler(Socket socket, AtomicBoolean running) {
            this.socket = socket;
            this.running = running;
        }

        @Override
        public void run() {
            try {
                System.out.println("Client connected.");
                try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
                    boolean connected = true;
                    while (connected){
                        String command = in.readLine();
                        System.out.println("Command received: " + command);
                        if (command == null) {
                            connected = false;
                        } else if (command.equals("shutdown")) {
                            running.set(false);
                            try (Socket tmpSocket = new Socket("localhost", 10101)) {}
                        }
                        // process other commands
                    }
                }
                System.out.println("Client disconnected.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

更新:更改示例以建立与服务器的连接以使其解除阻塞。

更新: 处理客户端断开连接的示例代码。感谢@user207421 强调了这一点(谢谢)。

更新:更改了示例代码以处理多个客户端套接字/线程。请注意,当您关闭当前刚刚打印到标准错误的套接字时,它将通过一个异常。您可能希望以不同的方式处理它。

更新:您可能还会发现模拟多个客户端连接的代码很有帮助:

public class Clients {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> new NormalClient().run());
        Thread thread2 = new Thread(() -> new NormalClient().run());
        Thread thread3 = new Thread(() -> new NormalClient().run());
        Thread thread4 = new Thread(() -> new NormalClient().run());
        Thread thread5 = new Thread(() -> new ShutdownClient().run());
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
    }
}

class NormalClient {
    void run() {
        try {
            try (Socket socket = new Socket("localhost", 10101);
                 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {

                for (int i = 0; i < 10; i++) {
                    out.write("hello " + i);
                    out.newLine();
                    out.flush();
                    sleep(1000);
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ShutdownClient {
    void run() {
        try {
            try (Socket socket = new Socket("localhost", 10101);
                 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {

                sleep(8000);
                out.write("shutdown");
                out.newLine();
                out.flush();
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

  • 好的,这几乎成功了。不幸的是,它在关闭之前正在等待另一条消息。也许是因为我不知道该怎么做的清除资源位(这有点像我第一次潜入多线程)。此外,该程序不会因某种原因停止运行(这就是最初的目的)。我希望我足够具体...
  • 基本上我期待Process finished with exit code 0,即使在第二次迭代之后也不会发生
  • @Crates 我预计callingThread 上的interrupt() 不起作用(某些Java API 在阻塞时无法中断)。所以callingThread 仍然被accept() 屏蔽。正如我在回答中提到的,您可能需要考虑与服务器建立连接,而不是使用callingThread.interrupt()
  • @Crates 看看更新。这应该会更好一些。
  • 此代码在对等端关闭连接时获得 NPE。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-21
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多