我正在编写我正在处理的应用程序的服务器组件。我正在使用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中的代码粘贴到此处并正确格式化吗?我使用代码按钮,但缩进仍然不正确,你可能已经注意到了。
答案 0 :(得分:0)
我建议你使用netty框架。因为它已经有处理这类问题的机制。看看this
答案 1 :(得分:-1)
您需要将连接视为字节流。在TCP中没有包这样的东西。服务器可能会向您发送100个字节,然后发送500个字节,但它可以一起到达接收器,一次一个字节,或者介于两者之间。因此,如果您的协议中有不同的消息,则第一个要求是您可以自己拆分它们,就好像您是从文件而不是套接字读取它们一样。在你将其纳入协议之前,这个问题是不可解决的。