繁体   English   中英

如何避免使用Java ServerSocket阻塞?

[英]How can I avoid blocking with Java ServerSocket?

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

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

我想如果只知道如何使用NIO设置服务器来监听端口,那么就可以实现localhost上的端口80,其余的都很容易实现。

干杯

这里有一个开始使用NIO的小例子。

它是一个侦听端口80和81的服务器,并打印标准输出上接收的所有内容。 收到以CLOSE开头的数据包后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;
            }
        }
    }
}

ObsSelectionKeyOP_ACCEPT ...)的字段是静态导入的:

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

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

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

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

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

许多框架,例如Apache MINANetty ,都是基于Java NIO实现的,以增强非阻塞IO编程。 我强烈建议他们让你的NIO编程变得快乐,而不是噩梦。 他们适合你的问题。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM