All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.code.yanf4j.nio.impl.NioTCPSession Maven / Gradle / Ivy

There is a newer version: 2.4.8
Show newest version
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 writeFuture) {
		WriteMessage message = new WriteMessageImpl(msg,
				(FutureImpl) 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) {
				decodeAndDispatch();
			} 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();
		}
	}

	private void decodeAndDispatch() {
		updateTimeStamp();
		this.readBuffer.flip();
		this.decode();
		this.readBuffer.compact();
	}

	/**
	 * Blocking read using temp selector
	 * 
	 * @return
	 * @throws ClosedChannelException
	 * @throws IOException
	 */
	protected final int blockingRead() throws ClosedChannelException,
			IOException {
		int n = 0;
		int readCount = 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());
						readCount += n;
						if (log.isDebugEnabled()) {
							log.debug("use temp selector read " + n + " bytes");
						}
					} while (n > 0 && this.readBuffer.hasRemaining());
					if (readCount > 0) {
						decodeAndDispatch();
					}
				}
			}
		} finally {
			if (tmpKey != null) {
				tmpKey.cancel();
				tmpKey = null;
			}
			if (readSelector != null) {
				// Cancel the key.
				readSelector.selectNow();
				SelectorFactory.returnSelector(readSelector);
			}
		}
		return readCount;
	}

	/**
	 * 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();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy