/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. * */ package com.github.zangxiaoqiang.io.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A NIO based TCP session, should be used by {@link NioTcpServer} and {@link NioTcpClient}. A TCP session is a * connection between a our server/client and the remote end-point. * * @author <a href="http://mina.apache.org">Apache MINA Project</a> * */ public class NioTcpSession implements SelectorListener { private static final Logger LOG = LoggerFactory.getLogger(NioTcpSession.class); /** the selector loop in charge of generating read/write events for this session */ private final SelectorLoop selectorLoop; /** the socket configuration */ /** The associated selectionKey */ private SelectionKey selectionKey; /** The Direct Buffer used to send data */ private ByteBuffer sendBuffer; /** The size of the buffer configured in the socket to send data */ private int sendBufferSize; private SocketChannel channel; MessageHandler msgHandler; /* No qualifier */ NioTcpSession(final NioTcpServer service, final SocketChannel channel, final SelectorLoop selectorLoop) { this.selectorLoop = selectorLoop; sendBufferSize = 1024; sendBuffer = ByteBuffer.allocateDirect(sendBufferSize); this.channel = channel; } /** * Get the underlying {@link SocketChannel} of this session * * @return the socket channel used by this session */ SocketChannel getSocketChannel() { return channel; } /** * {@inheritDoc} */ public InetSocketAddress getRemoteAddress() { if (channel == null) { return null; } final Socket socket = ((SocketChannel) channel).socket(); if (socket == null) { return null; } return (InetSocketAddress) socket.getRemoteSocketAddress(); } /** * {@inheritDoc} */ public InetSocketAddress getLocalAddress() { if (channel == null) { return null; } final Socket socket = ((SocketChannel) channel).socket(); if (socket == null) { return null; } return (InetSocketAddress) socket.getLocalSocketAddress(); } /** * {@inheritDoc} */ public void suspendRead() { // TODO throw new RuntimeException("Not implemented"); } /** * {@inheritDoc} */ public void suspendWrite() { // TODO throw new RuntimeException("Not implemented"); } /** * {@inheritDoc} */ protected int writeDirect(Object message) { try { // Check that we can write into the channel if (!isRegisteredForWrite()) { // We don't have pending writes return ((SocketChannel) channel).write((ByteBuffer) message); } else { return -1; } } catch (final IOException e) { LOG.error("Exception while reading : ", e); processException(e); return -1; } } /** * {@inheritDoc} */ /* protected ByteBuffer convertToDirectBuffer(WriteRequest writeRequest, boolean createNew) { ByteBuffer message = (ByteBuffer) writeRequest.getMessage(); if (!message.isDirect()) { int remaining = message.remaining(); if ((remaining > sendBufferSize) || createNew) { ByteBuffer directBuffer = ByteBuffer.allocateDirect(remaining); directBuffer.put(message); directBuffer.flip(); writeRequest.setMessage(directBuffer); return directBuffer; } else { sendBuffer.clear(); sendBuffer.put(message); sendBuffer.flip(); writeRequest.setMessage(sendBuffer); return sendBuffer; } } return message; }*/ /** * Set this session status as connected. To be called by the processor selecting/polling this session. */ void setConnected() { // if (!isCreated()) { // throw new RuntimeException("Trying to open a non created session"); // } // // state = SessionState.CONNECTED; // // if (connectFuture != null) { // connectFuture.complete(this); // // free some memory // connectFuture = null; // } // // processSessionOpen(); } /** * {@inheritDoc} */ protected void channelClose() { try { selectorLoop.unregister(this, channel); channel.close(); } catch (final IOException e) { LOG.error("Exception while closing the channel : ", e); processException(e); } } /** * {@inheritDoc} */ public void flushWriteQueue() { // register for write selectorLoop.modifyRegistration(false, !isReadSuspended(), true, this, channel, true); } /** * Process a read operation : read the data from the channel and push them to the chain. * * @param readBuffer The buffer that will contain the read data */ private void processRead(final ByteBuffer readBuffer) { try { LOG.debug("readable session : {}", this); // Read everything we can up to the buffer size System.out.println("Before read:" + readBuffer); final int readCount = ((SocketChannel) channel).read(readBuffer); LOG.debug("read {} bytes", readCount); if (readCount < 0) { // session closed by the remote peer LOG.debug("session closed by the remote peer"); close(true); } else if (readCount > 0) { // we have read some data // limit at the current position & rewind buffer back to start & // push to the chain readBuffer.flip(); // Plain message, not encrypted : go directly to the chain processMessageReceived(testChannel, readBuffer); } } catch (final IOException e) { LOG.error("Exception while reading : ", e); processException(e); } finally{ // And now, clear the buffer readBuffer.clear(); System.out.println("Clean buffer"); } } boolean isWritable = false; boolean isReadble = false; /** * {@inheritDoc} */ SocketChannel testChannel; public void ready(final boolean accept, boolean connect, final boolean read, final ByteBuffer readBuffer, final boolean write, SelectionKey key) { testChannel= (SocketChannel) key.channel(); if (LOG.isDebugEnabled()) { LOG.debug("session {} ready for accept={}, connect={}, read={}, write={}", new Object[] { this, accept, connect, read, write }); } isWritable = write; isReadble = read; if (connect) { try { boolean isConnected = ((SocketChannel) channel).finishConnect(); if (!isConnected) { LOG.error("unable to connect session {}", this); } else { // cancel current registration for connection selectionKey.cancel(); selectionKey = null; // Register for reading selectorLoop.register(false, false, true, false, this, channel, new RegistrationCallback() { public void done(SelectionKey selectionKey) { setConnected(); } }); } } catch (IOException e) { LOG.debug("Connection error, we cancel the future", e); } } if (read) { processRead(readBuffer); } if (write) { processWrite(selectorLoop); } if (accept) { throw new IllegalStateException("accept event should never occur on NioTcpSession"); } } void setSelectionKey(SelectionKey key) { this.selectionKey = key; } private void close(boolean b) { channelClose(); } private void processException(IOException e) { e.printStackTrace(); close(true); } private void processWrite(SelectorLoop selectorLoop2) { // TODO Auto-generated method stub } public void processMessageReceived(SocketChannel channel, ByteBuffer message) { // if(msgHandler!=null){ // msgHandler.processMessage(channel, message); // return; // } ByteBuffer original = message; ByteBuffer clone = ByteBuffer.allocate(original.capacity()); // copy from the beginning original.rewind(); clone.put(original); original.rewind(); clone.flip(); if(msgHandler!=null){ msgHandler.processMessage(channel, clone); } } private boolean isReadSuspended() { return isReadble; } private boolean isRegisteredForWrite() { return isWritable; } public void registeredMessageHandler(MessageHandler handler) { this.msgHandler = handler; } }