【发布时间】:2015-05-31 09:49:32
【问题描述】:
美好的一天。
我正在使用 NIO 多路复用,我不想在我的应用程序中分配额外的缓冲区,直到我确定套接字中有足够的字节来读取整个应用程序数据包。每个应用数据包包含 4 个字节的数据包长度(标头)和随后的数据包主体字节。如果可用,我想读取 4 个字节的数据包长度,然后如果可用,则读取数据包主体的字节。所以,代码看起来像这样:
private final ByteBuffer READ_BUFFER = ByteBuffer.wrap(new byte[READ_BUFFER_SIZE]);
...
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext())
{
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
Connection con = (Connection) key.attachment();
switch (key.readyOps())
{
case SelectionKey.OP_ACCEPT:
acceptConnection(key);
break;
case SelectionKey.OP_READ:
readPacket(key, con); //**
}
}
...
private final void readPacket(final SelectionKey key, final Connection con)
{
READ_BUFFER.clear();
int result = -2;
try
{
result = con.read(READ_BUFFER);
}
catch (IOException e) {}
//* packet processing goes here
}
在*中我们可以得到:
- 不到 4 个字节来读取标头;
- 4 个字节用于标头,但没有足够的字节用于数据包主体
- 足够的字节来读取 N 个数据包(标头 + 正文),但是 (N+1) 的一部分 包
在 3 种情况中的任何一种情况下,我都必须将读取的数据存储在额外的 ByteBuffer 中,因为 READ_BUFFER 将用于 ** 中的下一个连接。
我想做什么:
public class Connection
{
private final ByteChannel byteChannel;
private final InputStream in;
private int lastPacketSize = -1;
Connection(final Socket socket)
{
byteChannel = socket.getChannel();
in = socket.getInputStream();
}
int read(final ByteBuffer buf) throws IOException
{
return byteChannel.read(buf);
}
int available()
{
return in.available();
}
void setLastPacketSize(int size)
{
lastPacketSize = size;
}
int getLastPacketSize()
{
return lastPacketSize;
}
}
private final IntBuffer HEADER_BUFFER = IntBuffer.wrap(new int[1]);
private final void readPacket(final SelectionKey key, final Connection con)
{
int packetSize = con.getLastPacketSize();
if (packetSize < 0)
{
if (con.available() < 4) return;
int result = -2;
HEADER_BUFFER.clear();
try
{
result = con.read(HEADER_BUFFER);
}
catch (IOException e) {}
if (result != 4)
{
closeConnection(key);
return;
}
packetSize = HEADER_BUFFER.get();
con.setLastPacketSize(packetSize);
}
if (con.available() < packetSize) return;
result = -2;
READ_BUFFER.clear();
READ_BUFFER.limit(packetSize);
try
{
result = con.read(READ_BUFFER);
}
catch (IOException e) {}
if (result != packetSize)
{
closeConnection(key);
return;
}
con.setLastPacketSize(-1);
//packet processing goes here
}
我发现 Socket 的 InputStream 在 Linux 上使用 ioctl(fd, FIONREAD, pbytes)。在这种情况下我可以依赖 con.available() 还是会失败?如果它会失败,原因是什么?如何改进此代码以克服这些原因?
【问题讨论】: