private void doRead(SelectionKey key) {
        ReadAtta atta = (ReadAtta) key.attachment();
        SocketChannel ch = (SocketChannel) key.channel();
        try {
            if (atta.getState() == ReadAtta.state.READ_BY_USER) {
                // we should read header
                assert (atta.getReadBuffer() == null);
                headBuffer.clear();
                ch.read(headBuffer);
                assert (headBuffer.remaining() == 0);

                byte[] header = headBuffer.array();
                for (int i = 0; i < JMsg.HEADER_BYTE.length; i++) {
                    // FIXME close it & report to invoker
                    if (header[i] != JMsg.HEADER_BYTE[i])
                        throw new IllegalArgumentException("wrong header");
                }
                int size = JMsg.decode(headBuffer.array(), JMsg.HEADER.length());
                atta.setReadBuffer(ByteBuffer.allocate(size));
                atta.setState(ReadAtta.state.READ_HEADER);
            }

            ByteBuffer buffer;
            buffer = atta.getReadBuffer();
            assert (buffer != null);

            int read = ch.read(buffer);
            if (read == -1) {
                // remote entity shut the socket down cleanly.
                closeKey(key);
            } else if (read > 0 && !buffer.hasRemaining()) {
                buffer.flip(); // flip for read
                atta.getSocket().getRecvQueue().add(atta);
            }
        } catch (IOException e) { // the remote forcibly closed the connection
            closeKey(key);
        }
    }