Java:使用NIO读取可变大小的数据包

时间:2013-04-02 17:22:57

标签: java networking nio

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

[int] [length]
[byte] [操作码]
[byte ...] [payload]

我的方法是有两个独立的缓冲区:一个容量为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。除此之外,这个解决方案似乎并不干净。我觉得代码中有太多的逻辑,似乎有问题。任何人都可以为我提供一些修改或我的代码或完全不同的方法吗?我很感激。感谢。

在旁注中,任何人都可以在评论中发布一个很好的方法将Eclipse中的代码粘贴到此处并正确格式化吗?我使用代码按钮,但缩进仍然不正确,你可能已经注意到了。

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:-1)

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