code: int doIO(ByteBuffer buf, int ops) throws IOException {
      
      /* For now only one thread is allowed. If user want to read or write
       * from multiple threads, multiple streams could be created. In that
       * case multiple threads work as well as underlying channel supports it.
       */
      if (!buf.hasRemaining()) {
        throw new IllegalArgumentException("Buffer has no data left.");
        //or should we just return 0?
      }
  
      while (buf.hasRemaining()) {
        if (closed) {
          return -1;
        }
  
        try {
          int n = performIO(buf);
          if (n != 0) {
            // successful io or an error.
            return n;
          }
        } catch (IOException e) {
          if (!channel.isOpen()) {
            closed = true;
          }
          throw e;
        }
  
        //now wait for socket to be ready.
        int count = 0;
        try {
          count = selector.select(channel, ops, timeout);  
        } catch (IOException e) { //unexpected IOException.
          closed = true;
          throw e;
        } 
  
        if (count == 0) {
          throw new SocketTimeoutException(timeoutExceptionString(channel,
                                                                  timeout, ops));
        }
        // otherwise the socket should be ready for io.
      }
      
      return 0; // does not reach here.
    }
docs: Performs one IO and returns number of bytes read or written.
  It waits up to the specified timeout. If the channel is
  not read before the timeout, SocketTimeoutException is thrown.
  
  @param buf buffer for IO
  @param ops Selection Ops used for waiting. Suggested values:
  SelectionKey.OP_READ while reading and SelectionKey.OP_WRITE while
  writing.
  
  @return number of bytes read or written. negative implies end of stream.
  @throws IOException