 void doIO(SelectionKey k) throws InterruptedException {
        try {
            if (isSocketOpen() == false) { // socket未开启
                LOG.warn("trying to do i/o on a null socket for session:0x"
                         + Long.toHexString(sessionId));

                return;
            }
            if (k.isReadable()) { // key可读
                // 将内容从socket写入incoming缓冲
                int rc = sock.read(incomingBuffer);
                if (rc < 0) { // 流结束异常，无法从客户端读取数据
                    throw new EndOfStreamException(
                            "Unable to read additional data from client sessionid 0x"
                            + Long.toHexString(sessionId)
                            + ", likely client has closed socket");
                }
                if (incomingBuffer.remaining() == 0) { // 缓冲区已经写满
                    boolean isPayload;
                    // 读取下个请求
                    if (incomingBuffer == lenBuffer) { // start of next request
                        // 翻转缓冲区，可读
                        incomingBuffer.flip();
                        // 读取lenBuffer的前四个字节，当读取的是内容长度时则为true，否则为false
                        isPayload = readLength(k);
                        // 清除缓冲
                        incomingBuffer.clear();
                    } else { // 不等，因为在readLength中根据Len已经重新分配了incomingBuffer
                        // continuation
                        isPayload = true;
                    }
                    if (isPayload) { // 不为四个字母，为实际内容    // not the case for 4letterword
                        // 读取内容
                        readPayload();
                    }
                    else { // 四个字母，为四字母的命令
                        // four letter words take care
                        // need not do anything else
                        return;
                    }
                }
            }
            if (k.isWritable()) { // key可写
                // ZooLog.logTraceMessage(LOG,
                // ZooLog.CLIENT_DATA_PACKET_TRACE_MASK
                // "outgoingBuffers.size() = " +
                // outgoingBuffers.size());
                if (outgoingBuffers.size() > 0) {
                    // ZooLog.logTraceMessage(LOG,
                    // ZooLog.CLIENT_DATA_PACKET_TRACE_MASK,
                    // "sk " + k + " is valid: " +
                    // k.isValid());

                    /*
                     * This is going to reset the buffer position to 0 and the
                     * limit to the size of the buffer, so that we can fill it
                     * with data from the non-direct buffers that we need to
                     * send.
                     */
                    // 分配的直接缓冲
                    ByteBuffer directBuffer = factory.directBuffer;
                    // 清除缓冲
                    directBuffer.clear();

                    for (ByteBuffer b : outgoingBuffers) { // 遍历
                        if (directBuffer.remaining() < b.remaining()) { // directBuffer的剩余空闲长度小于b的剩余空闲长度
                            /*
                             * When we call put later, if the directBuffer is to
                             * small to hold everything, nothing will be copied,
                             * so we've got to slice the buffer if it's too big.
                             */
                            // 缩小缓冲至directBuffer的大小
                            b = (ByteBuffer) b.slice().limit(
                                    directBuffer.remaining());
                        }
                        /*
                         * put() is going to modify the positions of both
                         * buffers, put we don't want to change the position of
                         * the source buffers (we'll do that after the send, if
                         * needed), so we save and reset the position after the
                         * copy
                         */
                        // 记录b的当前position
                        int p = b.position();
                        // 将b写入directBuffer
                        directBuffer.put(b);
                        // 设置回b的原来的position
                        b.position(p);
                        if (directBuffer.remaining() == 0) { // 已经写满
                            break;
                        }
                    }
                    /*
                     * Do the flip: limit becomes position, position gets set to
                     * 0. This sets us up for the write.
                     */
                    // 翻转缓冲区，可读
                    directBuffer.flip();

                    // 将directBuffer的内容写入socket
                    int sent = sock.write(directBuffer);
                    ByteBuffer bb;

                    // Remove the buffers that we have sent
                    while (outgoingBuffers.size() > 0) { // outgoingBuffers中还存在Buffer
                        // 取队首元素，但并不移出
                        bb = outgoingBuffers.peek();
                        if (bb == ServerCnxnFactory.closeConn) { // 关闭连接，抛出异常
                            throw new CloseRequestException("close requested");
                        }
                        
                        // bb还剩余多少元素没有被发送
                        int left = bb.remaining() - sent;
                        if (left > 0) { // 存在元素未被发送
                            /*
                             * We only partially sent this buffer, so we update
                             * the position and exit the loop.
                             */
                            // 更新bb的position
                            bb.position(bb.position() + sent);
                            break;
                        }
                        // 发送包，调用ServerCnxn方法
                        packetSent();
                        /* We've sent the whole buffer, so drop the buffer */
                        // 已经发送完buffer的所有内容，移除buffer
                        sent -= bb.remaining();
                        outgoingBuffers.remove();
                    }
                    // ZooLog.logTraceMessage(LOG,
                    // ZooLog.CLIENT_DATA_PACKET_TRACE_MASK, "after send,
                    // outgoingBuffers.size() = " + outgoingBuffers.size());
                }

                synchronized(this.factory){ // 同步块
                    if (outgoingBuffers.size() == 0) { // outgoingBuffers不存在buffer
                        if (!initialized
                                && (sk.interestOps() & SelectionKey.OP_READ) == 0) { // 未初始化并且无读请求
                            throw new CloseRequestException("responded to info probe");
                        }
                        // 重置感兴趣的集合
                        sk.interestOps(sk.interestOps()
                                & (~SelectionKey.OP_WRITE));
                    } else { // 重置感兴趣的集合
                        sk.interestOps(sk.interestOps()
                                | SelectionKey.OP_WRITE);
                    }
                }
            }
        } catch (CancelledKeyException e) {
            LOG.warn("Exception causing close of session 0x"
                    + Long.toHexString(sessionId)
                    + " due to " + e);
            if (LOG.isDebugEnabled()) {
                LOG.debug("CancelledKeyException stack trace", e);
            }
            close();
        } catch (CloseRequestException e) {
            // expecting close to log session closure
            close();
        } catch (EndOfStreamException e) {
            LOG.warn("caught end of stream exception",e); // tell user why

            // expecting close to log session closure
            close();
        } catch (IOException e) {
            LOG.warn("Exception causing close of session 0x"
                    + Long.toHexString(sessionId)
                    + " due to " + e);
            if (LOG.isDebugEnabled()) {
                LOG.debug("IOException stack trace", e);
            }
            close();
        }
    }