Package com.google.code.yanf4j.nio.impl

Source Code of com.google.code.yanf4j.nio.impl.NioTCPSession

package com.google.code.yanf4j.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.yanf4j.buffer.IoBuffer;
import com.google.code.yanf4j.config.Configuration;
import com.google.code.yanf4j.core.EventType;
import com.google.code.yanf4j.core.WriteMessage;
import com.google.code.yanf4j.core.impl.FutureImpl;
import com.google.code.yanf4j.core.impl.WriteMessageImpl;
import com.google.code.yanf4j.nio.NioSessionConfig;
import com.google.code.yanf4j.util.ByteBufferUtils;
import com.google.code.yanf4j.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=" + this.sessionTimeout + ",this.timestamp="
          + this.lastOperationTimeStamp.get() + ",current="
          + System.currentTimeMillis());
    }
    return this.sessionTimeout <= 0 ? false : System.currentTimeMillis()
        - this.lastOperationTimeStamp.get() >= this.sessionTimeout;
  }

  public NioTCPSession(NioSessionConfig sessionConfig, int readRecvBufferSize) {
    super(sessionConfig);
    if (this.selectableChannel != null && this.getRemoteSocketAddress() != null) {
      this.loopback = this.getRemoteSocketAddress().getAddress()
          .isLoopbackAddress();
    }
    this.setReadBuffer(IoBuffer.allocate(readRecvBufferSize));
    this.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 (this.useBlockingWrite) {
      return this.blockingWrite(this.selectableChannel, message, writeBuffer);
    } else {
      while (true) {
        long n = this.doRealWrite(this.selectableChannel, writeBuffer);
        if (n > 0) {
          this.statistics.statisticsWrite(n);
          this.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 (this.remoteAddress == null) {
      this.remoteAddress = (InetSocketAddress) ((SocketChannel) this.selectableChannel)
          .socket().getRemoteSocketAddress();
    }
    return this.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 = this.doRealWrite(channel, writeBuffer);
        if (len > 0) {
          attempts = 0;
          bytesProduced += len;
          this.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);
      }
    }
    this.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(this.encoder.encode(message.getMessage(), this));
    }
    return message;
  }

  @Override
  protected void readFromBuffer() {
    if (!this.readBuffer.hasRemaining()) {
      if (this.readBuffer.capacity() < Configuration.MAX_READ_BUFFER_SIZE) {
        this.readBuffer = IoBuffer.wrap(ByteBufferUtils
            .increaseBufferCapatity(this.readBuffer.buf()));
      } else {
        // buffer's capacity is greater than maxium
        return;
      }
    }
    if (this.closed) {
      return;
    }
    int n = -1;
    int readCount = 0;
    try {
      while ((n = ((ReadableByteChannel) this.selectableChannel)
          .read(this.readBuffer.buf())) > 0) {
        readCount += n;
      }
      if (readCount > 0) {
        this.readBuffer.flip();
        this.decode();
        this.readBuffer.compact();
      } else if (readCount == 0
          && !((SocketChannel) this.selectableChannel).socket()
              .isInputShutdown() && this.useBlockingRead) {
        n = this.blockingRead();
        if (n > 0) {
          readCount += n;
        }
      }
      if (n < 0) { // Connection closed
        this.close();
      } else {
        this.selectorManager.registerSession(this, EventType.ENABLE_READ);
      }
      if (log.isDebugEnabled()) {
        log.debug("read " + readCount + " bytes from channel");
      }
    } catch (ClosedChannelException e) {
      // ignore exception
      this.close();
    } catch (Throwable e) {
      this.onException(e);
      this.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 (this.selectableChannel.isOpen()) {
        tmpKey = this.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) this.selectableChannel)
                .read(this.readBuffer.buf());
            if (log.isDebugEnabled()) {
              log.debug("use temp selector read " + n + " bytes");
            }
          } while (n > 0 && this.readBuffer.hasRemaining());
          this.readBuffer.flip();
          this.decode();
          this.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 = this.readBuffer.remaining();
    while (this.readBuffer.hasRemaining()) {
      try {
        message = this.decoder.decode(this.readBuffer, this);
        if (message == null) {
          break;
        } else {
          if (this.statistics.isStatistics()) {
            this.statistics
                .statisticsRead(size - this.readBuffer.remaining());
            size = this.readBuffer.remaining();
          }
        }
        this.dispatchReceivedMessage(message);
      } catch (Exception e) {
        this.onException(e);
        log.error("Decode error", e);
        super.close();
        break;
      }
    }
  }

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

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

    }
    this.unregisterSession();
    this.unregisterChannel();
  }

}
TOP

Related Classes of com.google.code.yanf4j.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.