private boolean flushNow(S session, long currentTime) {
    if (!session.isConnected()) {
      scheduleRemove(session);
      return false;
    }

    final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();

    final WriteRequestQueue writeRequestQueue = session.getWriteRequestQueue();

    // Set limitation for the number of written bytes for read-write
    // fairness. I used maxReadBufferSize * 3 / 2, which yields best
    // performance in my experience while not breaking fairness much.
    final int maxWrittenBytes =
        session.getConfig().getMaxReadBufferSize()
            + (session.getConfig().getMaxReadBufferSize() >>> 1);
    int writtenBytes = 0;
    WriteRequest req = null;

    try {
      // Clear OP_WRITE
      setInterestedInWrite(session, false);

      do {
        // Check for pending writes.
        req = session.getCurrentWriteRequest();

        if (req == null) {
          req = writeRequestQueue.poll(session);

          if (req == null) {
            break;
          }

          session.setCurrentWriteRequest(req);
        }

        int localWrittenBytes = 0;
        Object message = req.getMessage();

        if (message instanceof IoBuffer) {
          localWrittenBytes =
              writeBuffer(
                  session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);

          if ((localWrittenBytes > 0) && ((IoBuffer) message).hasRemaining()) {
            // the buffer isn't empty, we re-interest it in writing
            writtenBytes += localWrittenBytes;
            setInterestedInWrite(session, true);
            return false;
          }
        } else if (message instanceof FileRegion) {
          localWrittenBytes =
              writeFile(
                  session, req, hasFragmentation, maxWrittenBytes - writtenBytes, currentTime);

          // Fix for Java bug on Linux
          // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5103988
          // If there's still data to be written in the FileRegion,
          // return 0 indicating that we need
          // to pause until writing may resume.
          if ((localWrittenBytes > 0) && (((FileRegion) message).getRemainingBytes() > 0)) {
            writtenBytes += localWrittenBytes;
            setInterestedInWrite(session, true);
            return false;
          }
        } else {
          throw new IllegalStateException(
              "Don't know how to handle message of type '"
                  + message.getClass().getName()
                  + "'.  Are you missing a protocol encoder?");
        }

        if (localWrittenBytes == 0) {
          // Kernel buffer is full.
          setInterestedInWrite(session, true);
          return false;
        }

        writtenBytes += localWrittenBytes;

        if (writtenBytes >= maxWrittenBytes) {
          // Wrote too much
          scheduleFlush(session);
          return false;
        }
      } while (writtenBytes < maxWrittenBytes);
    } catch (Exception e) {
      if (req != null) {
        req.getFuture().setException(e);
      }

      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
      return false;
    }

    return true;
  }
  private void read(S session) {
    IoSessionConfig config = session.getConfig();
    int bufferSize = config.getReadBufferSize();
    IoBuffer buf = IoBuffer.allocate(bufferSize);

    final boolean hasFragmentation = session.getTransportMetadata().hasFragmentation();

    try {
      int readBytes = 0;
      int ret;

      try {
        if (hasFragmentation) {

          while ((ret = read(session, buf)) > 0) {
            readBytes += ret;

            if (!buf.hasRemaining()) {
              break;
            }
          }
        } else {
          ret = read(session, buf);

          if (ret > 0) {
            readBytes = ret;
          }
        }
      } finally {
        buf.flip();
      }

      if (readBytes > 0) {
        IoFilterChain filterChain = session.getFilterChain();
        filterChain.fireMessageReceived(buf);
        buf = null;

        if (hasFragmentation) {
          if (readBytes << 1 < config.getReadBufferSize()) {
            session.decreaseReadBufferSize();
          } else if (readBytes == config.getReadBufferSize()) {
            session.increaseReadBufferSize();
          }
        }
      }

      if (ret < 0) {
        scheduleRemove(session);
      }
    } catch (Throwable e) {
      if (e instanceof IOException) {
        if (!(e instanceof PortUnreachableException)
            || !AbstractDatagramSessionConfig.class.isAssignableFrom(config.getClass())
            || ((AbstractDatagramSessionConfig) config).isCloseOnPortUnreachable()) {
          scheduleRemove(session);
        }
      }

      IoFilterChain filterChain = session.getFilterChain();
      filterChain.fireExceptionCaught(e);
    }
  }