Package com.google.code.hs4j.network.nio.impl

Source Code of com.google.code.hs4j.network.nio.impl.NioTCPSession

package com.google.code.hs4j.network.nio.impl;

/**
*Copyright [2008-2009] [dennis zhuang]
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*             http://www.apache.org/licenses/LICENSE-2.0
*Unless required by applicable law or agreed to in writing,
*software distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
*either express or implied. See the License for the specific language governing permissions and limitations under the License
*/
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Future;

import com.google.code.hs4j.network.buffer.IoBuffer;
import com.google.code.hs4j.network.config.Configuration;
import com.google.code.hs4j.network.core.EventType;
import com.google.code.hs4j.network.core.WriteMessage;
import com.google.code.hs4j.network.core.impl.FutureImpl;
import com.google.code.hs4j.network.core.impl.WriteMessageImpl;
import com.google.code.hs4j.network.nio.NioSessionConfig;
import com.google.code.hs4j.network.util.ByteBufferUtils;
import com.google.code.hs4j.network.util.SelectorFactory;

/**
* Nio tcp connection
*
* @author dennis
*
*/
public class NioTCPSession extends AbstractNioSession {
  private InetSocketAddress remoteAddress;

  @Override
  public final boolean isExpired() {
    if (log.isDebugEnabled()) {
      log.debug("sessionTimeout=" + sessionTimeout + ",this.timestamp="
          + lastOperationTimeStamp.get() + ",current="
          + System.currentTimeMillis());
    }
    return sessionTimeout <= 0 ? false : System.currentTimeMillis()
        - lastOperationTimeStamp.get() >= sessionTimeout;
  }

  public NioTCPSession(NioSessionConfig sessionConfig, int readRecvBufferSize) {
    super(sessionConfig);
    if (selectableChannel != null && getRemoteSocketAddress() != null) {
      loopback = getRemoteSocketAddress().getAddress()
          .isLoopbackAddress();
    }
    setReadBuffer(IoBuffer.allocate(readRecvBufferSize));
    onCreated();
  }

  @Override
  protected Object writeToChannel(WriteMessage message) throws IOException {
    if (message.getWriteFuture() != null && !message.isWriting()
        && message.getWriteFuture().isCancelled()) {
      return message.getMessage();
    }
    if (message.getWriteBuffer() == null) {
      if (message.getWriteFuture() != null) {
        message.getWriteFuture().setResult(Boolean.TRUE);
      }
      return message.getMessage();
    }
    IoBuffer writeBuffer = message.getWriteBuffer();
    // begin writing
    message.writing();
    if (useBlockingWrite) {
      return blockingWrite(selectableChannel, message, writeBuffer);
    } else {
      while (true) {
        long n = doRealWrite(selectableChannel, writeBuffer);
        if (n > 0) {
          statistics.statisticsWrite(n);
          scheduleWritenBytes.addAndGet(0 - n);
        }
        if (writeBuffer == null || !writeBuffer.hasRemaining()) {
          if (message.getWriteFuture() != null) {
            message.getWriteFuture().setResult(Boolean.TRUE);
          }
          return message.getMessage();
        } else if (n == 0) {
          // have more data, but the buffer is full,
          // wait next time to write
          return null;
        }
      }
    }

  }

  public InetSocketAddress getRemoteSocketAddress() {
    if (remoteAddress == null) {
      remoteAddress = (InetSocketAddress) ((SocketChannel) selectableChannel)
          .socket().getRemoteSocketAddress();
    }
    return remoteAddress;
  }

  /**
   * Blocking write using temp selector
   *
   * @param channel
   * @param message
   * @param writeBuffer
   * @return
   * @throws IOException
   * @throws ClosedChannelException
   */
  protected final Object blockingWrite(SelectableChannel channel,
      WriteMessage message, IoBuffer writeBuffer) throws IOException,
      ClosedChannelException {
    SelectionKey tmpKey = null;
    Selector writeSelector = null;
    int attempts = 0;
    int bytesProduced = 0;
    try {
      while (writeBuffer.hasRemaining()) {
        long len = doRealWrite(channel, writeBuffer);
        if (len > 0) {
          attempts = 0;
          bytesProduced += len;
          statistics.statisticsWrite(len);
        } else {
          attempts++;
          if (writeSelector == null) {
            writeSelector = SelectorFactory.getSelector();
            if (writeSelector == null) {
              // Continue using the main one.
              continue;
            }
            tmpKey = channel.register(writeSelector,
                SelectionKey.OP_WRITE);
          }
          if (writeSelector.select(1000) == 0) {
            if (attempts > 2) {
              throw new IOException("Client disconnected");
            }
          }
        }
      }
      if (!writeBuffer.hasRemaining() && message.getWriteFuture() != null) {
        message.getWriteFuture().setResult(Boolean.TRUE);
      }
    } finally {
      if (tmpKey != null) {
        tmpKey.cancel();
        tmpKey = null;
      }
      if (writeSelector != null) {
        // Cancel the key.
        writeSelector.selectNow();
        SelectorFactory.returnSelector(writeSelector);
      }
    }
    scheduleWritenBytes.addAndGet(0 - bytesProduced);
    return message.getMessage();
  }

  @Override
  protected WriteMessage wrapMessage(Object msg, Future<Boolean> writeFuture) {
    WriteMessage message = new WriteMessageImpl(msg,
        (FutureImpl<Boolean>) writeFuture);
    if (message.getWriteBuffer() == null) {
      message.setWriteBuffer(encoder.encode(message.getMessage(), this));
    }
    return message;
  }

  @Override
  protected void readFromBuffer() {
    if (!readBuffer.hasRemaining()) {
      if (readBuffer.capacity() < Configuration.MAX_READ_BUFFER_SIZE) {
        readBuffer = IoBuffer.wrap(ByteBufferUtils
            .increaseBufferCapatity(readBuffer.buf()));
      } else {
        // buffer's capacity is greater than maxium
        return;
      }
    }
    if (closed) {
      return;
    }
    int n = -1;
    int readCount = 0;
    try {
      while ((n = ((ReadableByteChannel) selectableChannel)
          .read(readBuffer.buf())) > 0) {
        readCount += n;
      }
      if (readCount > 0) {
        readBuffer.flip();
        decode();
        readBuffer.compact();
      } else if (readCount == 0
          && !((SocketChannel) selectableChannel).socket()
              .isInputShutdown() && useBlockingRead) {
        n = blockingRead();
        if (n > 0) {
          readCount += n;
        }
      }
      if (n < 0) { // Connection closed
        close();
      } else {
        selectorManager.registerSession(this, EventType.ENABLE_READ);
      }
      if (log.isDebugEnabled()) {
        log.debug("read " + readCount + " bytes from channel");
      }
    } catch (ClosedChannelException e) {
      // ignore exception
      close();
    } catch (Throwable e) {
      onException(e);
      close();
    }
  }

  /**
   * Blocking read using temp selector
   *
   * @return
   * @throws ClosedChannelException
   * @throws IOException
   */
  protected final int blockingRead() throws ClosedChannelException,
      IOException {
    int n = 0;
    Selector readSelector = SelectorFactory.getSelector();
    SelectionKey tmpKey = null;
    try {
      if (selectableChannel.isOpen()) {
        tmpKey = selectableChannel.register(readSelector, 0);
        tmpKey.interestOps(tmpKey.interestOps() | SelectionKey.OP_READ);
        int code = readSelector.select(500);
        tmpKey
            .interestOps(tmpKey.interestOps()
                & ~SelectionKey.OP_READ);
        if (code > 0) {
          do {
            n = ((ReadableByteChannel) selectableChannel)
                .read(readBuffer.buf());
            if (log.isDebugEnabled()) {
              log.debug("use temp selector read " + n + " bytes");
            }
          } while (n > 0 && readBuffer.hasRemaining());
          readBuffer.flip();
          decode();
          readBuffer.compact();
        }
      }
    } finally {
      if (tmpKey != null) {
        tmpKey.cancel();
        tmpKey = null;
      }
      if (readSelector != null) {
        // Cancel the key.
        readSelector.selectNow();
        SelectorFactory.returnSelector(readSelector);
      }
    }
    return n;
  }

  /**
   * Decode buffer
   */
  @Override
  public void decode() {
    Object message;
    int size = readBuffer.remaining();
    while (readBuffer.hasRemaining()) {
      try {
        message = decoder.decode(readBuffer, this);
        if (message == null) {
          break;
        } else {
          if (statistics.isStatistics()) {
            statistics
                .statisticsRead(size - readBuffer.remaining());
            size = readBuffer.remaining();
          }
        }
        dispatchReceivedMessage(message);
      } catch (Exception e) {
        onException(e);
        log.error("Decode error", e);
        super.close();
        break;
      }
    }
  }

  public Socket socket() {
    return ((SocketChannel) selectableChannel).socket();
  }

  @Override
  protected final void closeChannel() throws IOException {
    flush0();
    // try to close output first
    Socket socket = ((SocketChannel) selectableChannel).socket();
    try {
      if (!socket.isClosed() && !socket.isOutputShutdown()) {
        socket.shutdownOutput();
      }
      if (!socket.isClosed() && !socket.isInputShutdown()) {
        socket.shutdownInput();
      }
    } catch (IOException e) {
    }
    try {
      socket.close();
    } catch (IOException e) {

    }
    unregisterSession();
    unregisterChannel();
  }

}
TOP

Related Classes of com.google.code.hs4j.network.nio.impl.NioTCPSession

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.