13

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

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

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

干杯

4

3 回答 3

15

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

它是一个侦听端口 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;
            }
        }
    }
}

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

import static java.nio.channels.SelectionKey.*;
于 2010-01-23T18:47:12.347 回答
8

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

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

于 2010-01-23T06:30:14.283 回答
7

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

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

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

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

于 2010-01-23T05:45:50.127 回答