如何避免 Java ServerSocket 阻塞?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2122263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 19:33:57  来源:igfitidea点击:

How can I avoid blocking with Java ServerSocket?

javasocketsconcurrencynonblocking

提问by ping

Im working on a socket listener that has to listen on 2 ports for 2 types of data( port 80 and port 81). These data are very similar as in the kind of operations that are performed on the data and are just different because they arrive n different ports. I went ahead and coded an implementation using Java's ServerSocket class, only to realize later that the accept() method of the ServerSocket class is block and my implementation cant afford that. So now i was thinking of implementing the same using Java NIO but after having gone through some tutorials i think i am more confused than how i started. It would be great if someone here could walk me through the whole process, even of it be in pseudo code or jus technical "what to do next"'s. This is what i plan to achieve.

我正在研究一个套接字侦听器,该侦听器必须在 2 个端口上侦听 2 种类型的数据(端口 80 和端口 81)。这些数据与对数据执行的操作类型非常相似,只是因为它们到达不同的端口而有所不同。我继续使用 Java 的 ServerSocket 类编写了一个实现,后来才意识到 ServerSocket 类的 accept() 方法是块,我的实现负担不起。所以现在我正在考虑使用 Java NIO 实现相同的功能,但是在阅读了一些教程之后,我认为我比我开始时更困惑。如果这里有人可以引导我完成整个过程,即使是伪代码或纯技术性的“下一步该做什么”,也很好。这就是我计划实现的目标。

Listen, like for ever on 2 ports by calling 2 similar threads.(non blocking) A remote device from some network location connects, sends data and then disconnects.

通过调用 2 个类似线程在 2 个端口上进行监听,就像永远一样。(非阻塞)来自某个网​​络位置的远程设备连接、发送数据然后断开连接。

I think if only knowledge of how NIO can be used to set up a server to listen on a port say port 80, on localhost,is achieved, the rest is all pretty easy to implement.

我认为,如果只了解如何使用 NIO 来设置服务器以侦听端口,例如 localhost 上的端口 80,那么其余的都很容易实现。

Cheers

干杯

采纳答案by erickson

NIO is called for when you need to scale to many thousands of simultaneous connections.

当您需要扩展到数千个同时连接时需要 NIO。

Otherwise, I'd suggest using multiple threads. For each port (and its corresponding ServerSocket), create a thread that calls accept()in a loop. These calls will block, but that is alright because other threads are running, taking care of any available tasks.

否则,我建议使用多个线程。对于每个端口(及其对应的ServerSocket),创建accept()一个循环调用的线程。这些调用会阻塞,但没关系,因为其他线程正在运行,负责处理任何可用任务。

When a new Socketis accepted, create another thread that is dedicated to that connection. It depends on the application, but typically this thread will read from the socket (a blocking operation), and perform the requested operation, writing the results back to the socket.

当一个新的Socket被接受时,创建另一个专用于该连接的线程。这取决于应用程序,但通常该线程将从套接字读取(阻塞操作),并执行请求的操作,将结果写回套接字。

This architecture will scale to many hundreds of connections on most desktop platforms. And the programming model is fairly simple, as long as each connection is self-contained and independent of the others (this avoids concurrency issues). Introducing NIO will provide more scalability, but requires a lot of complexity.

这种架构将扩展到大多数桌面平台上的数百个连接。并且编程模型相当简单,只要每个连接都是自包含的并且独立于其他连接(这样可以避免并发问题)。引入 NIO 将提供更多的可扩展性,但需要很多复杂性。

回答by user85421

Here a little example to get started with NIO.

这里有一个 NIO 入门的小例子。

It's a server listening on ports 80 and 81 and printing everything that is received on standard output. A connection is closed after receiving a packet starting with CLOSE; the whole server is shutdown after receiving a packet starting with QUIT. Missing the sending part and error handling could be a bit better. :-)

它是一个侦听端口 80 和 81 并打印标准输出上接收到的所有内容的服务器。接收到以CLOSE;开头的数据包后关闭连接;收到以 开头的数据包后,整个服务器将关闭QUIT。缺少发送部分和错误处理可能会好一点。:-)

public static void main() throws IOException {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    Selector selector = Selector.open();

    ServerSocketChannel server1 = ServerSocketChannel.open();
    server1.configureBlocking(false);
    server1.socket().bind(new InetSocketAddress(80));
    server1.register(selector, OP_ACCEPT);

    ServerSocketChannel server2 = ServerSocketChannel.open();
    server2.configureBlocking(false);
    server2.socket().bind(new InetSocketAddress(81));
    server2.register(selector, OP_ACCEPT);

    while (true) {
        selector.select();
        Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
        while (iter.hasNext()) {
            SocketChannel client;
            SelectionKey key = iter.next();
            iter.remove();

            switch (key.readyOps()) {
                case OP_ACCEPT:
                    client = ((ServerSocketChannel) key.channel()).accept();
                    client.configureBlocking(false);
                    client.register(selector, OP_READ);
                    break;
                case OP_READ:
                    client = (SocketChannel) key.channel();
                    buffer.clear();
                    if (client.read(buffer) != -1) {
                        buffer.flip();
                        String line = new String(buffer.array(), buffer.position(), buffer.remaining());
                        System.out.println(line);
                        if (line.startsWith("CLOSE")) {
                            client.close();
                        } else if (line.startsWith("QUIT")) {
                            for (SelectionKey k : selector.keys()) {
                                k.cancel();
                                k.channel().close();
                            }
                            selector.close();
                            return;
                        }
                    } else {
                        key.cancel();
                    }
                    break;
                default:
                    System.out.println("unhandled " + key.readyOps());
                    break;
            }
        }
    }
}

Obs: the fields of SelectionKey(OP_ACCEPT...) are statically imported:

ObsSelectionKey( OP_ACCEPT...)的字段是静态导入的:

import static java.nio.channels.SelectionKey.*;

回答by Amir Moghimi

Many frameworks, such as Apache MINAand Netty, have been implemented based on Java NIO to boost non-blocking IO programming. I strongly recommend them to make your NIO programming a joy, rather than a nightmare. They fit your problem.

Apache MINANetty等许多框架已经基于 Java NIO 实现,以提升非阻塞 IO 编程。我强烈推荐他们让你的 NIO 编程成为一种乐趣,而不是一场噩梦。它们适合您的问题。

In addition, try to use an efficient protocol both in transport message size and encode/decode (serialize/deserialize) performance. Google Protocol Buffersis a reliable solution in this area. Also take a look at Kryoand KryoNet. They can be helpful.

此外,尝试在传输消息大小和编码/解码(序列化/反序列化)性能方面使用有效的协议。Google Protocol Buffers是这方面的可靠解决方案。还可以看看KryoKryoNet。他们可以提供帮助。