美好的一天。具有InputStream.available的Java NIO
我正在使用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例由於READ_BUFFER將用於**中的下一個連接,因此我必須將附帶的數據存儲在其他ByteBuffer中。
我想要做什麼:
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()嗎,否則它可能會失敗?如果它可能失敗,原因是什麼以及如何改進這些代碼來克服這些原因?
我的想法是隻讀取數據包長度字節和進程數據包,其餘部分將保留在套接字緩衝區中。在這種情況下,我只需要長度> = packetLength的緩衝區。如果我的客戶端太多,並且這些客戶端產生太多數據包,它們將填充套接字緩衝區,然後填充應用程序級緩衝區,然後TCP流量控制將要求客戶端停止發送更多數據。所以,這些客戶端都需要額外的內存。 – user2289482