带有InputStream.available()的Java NIO

时间:2015-05-31 09:49:33

标签: java sockets 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) 包
  4. 在3种情况中的任何一种情况下,我必须将readed数据存储在附加的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(),否则会失败?如果它可能失败的原因是什么以及如何改进此代码以克服这些原因?

1 个答案:

答案 0 :(得分:0)

如果您使用非阻止模式,则根本无法使用InputStream。你不可能运行这段代码。

你要做的事情并没有多大意义。每次OP_READ触发时都要读取,直到有足够的数据要处理。