我正在编写我正在处理的应用程序的服务器组件。我正在使用 NIO 来处理网络,但我正在努力寻找一种有效的方式来处理阅读。客户端应用程序可以向服务器发送大小差异很大的数据包。它可以是 100 字节的文本消息,也可以是 500 KB 的图片,并且服务器需要能够处理这些数据包。通常,我会创建一个足够大的缓冲区以包含最大可能的数据包,但在这个应用程序的情况下,这意味着我可能必须有兆字节的缓冲区每个客户端的大小,这会吞噬内存。在和我的朋友讨论之后,我决定我想像使用旧 IO 一样处理这个问题。这意味着我将为每个数据包分配一个新缓冲区,该缓冲区的容量与传入数据包的长度完全相同。我的数据包结构如下:
[int] [length]
[byte] [opcode]
[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();
}
}
}
我看到了一些问题。一方面,我有时会在线收到 NullPointerException socketChannel.read(readBuffer)
。除此之外,这个解决方案似乎并不干净。感觉代码里逻辑太多,好像有问题。有人可以为我提供一些修改或我的代码或完全不同的方法吗?我会很感激的。谢谢。
附带说明一下,任何人都可以在评论中发布一种将 Eclipse 中的代码粘贴到此处并正确格式化的好方法吗?我使用了代码按钮,但缩进仍然不正确,您可能已经注意到了。