【问题标题】:Java: reading variable-size packets using NIOJava:使用 NIO 读取可变大小的数据包
【发布时间】:2013-04-02 17:22:56
【问题描述】:

我正在编写我正在处理的应用程序的服务器组件。我正在使用 NIO 来处理网络,但我正在努力寻找一种有效的方式来处理阅读。客户端应用程序可以向服务器发送大小差异很大的数据包。它可能是 100 字节的文本消息,或 500 KB 的图片,服务器需要能够处理这些数据包。通常,我会创建一个足够大的缓冲区以包含最大可能的数据包,但在这个应用程序的情况下,这意味着我可能必须为每个客户端设置 兆字节大小的缓冲区,那会吞噬记忆。在与我的朋友讨论后,我决定我想像使用旧 IO 一样处理这个问题。这意味着我将为每个数据包分配一个新缓冲区,该缓冲区的容量与传入数据包的长度完全相同。我的数据包结构如下:

[int] [长度]
[字节] [操作码]
[字节...] [有效负载]

我的方法是有两个单独的缓冲区:一个容量为 4 的缓冲区来读取长度,另一个缓冲区将不断地以不同的容量重新初始化 - 数据包的长度。这是我想出的代码:

User user = (User) selectionKey.attachment();
SocketChannel socketChannel = user.getSocketChannel();
ByteBuffer readBuffer = user.getReadBuffer();
ByteBuffer lengthBuffer = user.getLengthBuffer();
/*
 * Read as many packets as possible.
 */
while (true) {
    /*
     * Read the length if it has not been read yet.
     */
    if (lengthBuffer.hasRemaining()) {
    socketChannel.read(lengthBuffer);
    /*
     * If the length could not be read, stop reading and wait for
     * more data to arrive.
     */
    if (lengthBuffer.hasRemaining()) {
        break;
    }
    } else {
    /*
     * Create a read buffer if one has not been created for the
     * incoming packet.
     */
    if (readBuffer == null) {
        lengthBuffer.flip();
        user.setReadBuffer(ByteBuffer.allocate(lengthBuffer
            .getInt()));
    }
    /*
     * Attempt to read the packet.
     */
    socketChannel.read(readBuffer);
    /*
     * If the packet was not completely read, then stop reading
     * altogether because all of the data has not been received yet.
     */
    if (readBuffer.hasRemaining()) {
        break;
    } else {
        /*
         * Otherwise, handle the data and prepare the buffers for
         * the next packet read.
         */
        readBuffer.flip();
        user.handleData();
        user.setReadBuffer(null);
        lengthBuffer.clear();
    }
    }
}

我看到了一些问题。一方面,我有时会在socketChannel.read(readBuffer) 行收到 NullPointerException。除此之外,这个解决方案似乎并不干净。感觉代码里逻辑太多,好像有问题。有人可以为我提供一些修改或我的代码或完全不同的方法吗?我会很感激的。谢谢。

顺便说一句,任何人都可以在 cmets 中发布一种将 Eclipse 中的代码粘贴到此处并正确格式化的好方法吗?我使用了代码按钮,但缩进仍然不正确,您可能已经注意到了。

【问题讨论】:

    标签: java networking nio


    【解决方案1】:

    我会推荐你​​使用 netty 框架。因为它已经有处理这类问题的机制。看看this

    【讨论】:

    • 嗯...我确实认为使用库可以节省我一些时间。
    【解决方案2】:

    您需要将连接视为字节流。 TCP 中没有数据包这样的东西。服务器可能会向您发送 100 个字节,然后是 500 个字节,但它可能会一起到达接收器,一次一个字节,或者介于两者之间。因此,如果您的协议中有不同的消息,第一个要求是您能够自己拆分它们,就好像您是从文件而不是套接字中读取它们一样。在您将其内置到您的协议中之前,问题是无法解决的。

    【讨论】:

    • 我确实以相同的方式构建所有数据包。我知道一旦我读取了长度,我就需要读取长度字节数。一旦读取,我知道接下来读取的 4 个字节将是另一个长度,依此类推。我遇到的问题实际上是编写高效且有效的代码来完成该过程。
    猜你喜欢
    • 1970-01-01
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-19
    • 2015-07-13
    相关资源
    最近更新 更多