2015-05-31 55 views
0

美好的一天。具有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 
} 

在*,我們可以得到:

  1. 少於4個字節讀取頭;
  2. 4個字節用於報頭,但不用於分組體
  3. 足夠的字節來讀取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()嗎,否則它可能會失敗?如果它可能失敗,原因是什麼以及如何改進這些代碼來克服這些原因?

回答

0

如果您使用的是非阻塞模式,則根本不能使用InputStream。你不能運行這段代碼。

你想要做的事情沒有多少意義。每次OP_READ觸發時都要閱讀,直到您有足夠的數據進行處理。

+0

我的想法是隻讀取數據包長度字節和進程數據包,其餘部分將保留在套接字緩衝區中。在這種情況下,我只需要長度> = packetLength的緩衝區。如果我的客戶端太多,並且這些客戶端產生太多數據包,它們將填充套接字緩衝區,然後填充應用程序級緩衝區,然後TCP流量控制將要求客戶端停止發送更多數據。所以,這些客戶端都需要額外的內存。 – user2289482