/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package com.talent.nio.communicate.receive;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.talent.nio.api.Config;
import com.talent.nio.communicate.ChannelContext;
import com.talent.nio.communicate.ChannelContext.ConnectionState;
import com.talent.nio.communicate.RemoteNode;
import com.talent.nio.communicate.handler.intf.PacketHandlerIntf;
import com.talent.nio.communicate.intf.DecoderIntf;
import com.talent.nio.communicate.send.SendUtils;
import com.talent.nio.communicate.server.ServerContext;
import com.talent.nio.communicate.util.NioProxy;
import com.talent.nio.connmgr.ConnectionManager;
import com.talent.nio.utils.SystemTimer;
import com.talent.platform.threadpool.SynThreadPoolExecutor;
import com.talent.platform.threadpool.intf.SynRunnableIntf;
import com.talent.platform.threadpool.monitor.ThreadPoolMonitor;
/**
*
* @author 谭耀武
* @date 2011-08-04
*
*/
public class TcpListener
{
private static final Logger log = LoggerFactory.getLogger(TcpListener.class);
private final Selector selector;
private SynThreadPoolExecutor<SynRunnableIntf> synThreadPoolExecutor = null;
private static Map<SocketChannel, ChannelContext> mapOfSocketChannelAndChannelContext = new ConcurrentHashMap<SocketChannel, ChannelContext>();
/**
* 用于服务监听。 key: port, value: protocol
*/
private final static Map<ServerSocketChannel, ServerContext> mapOfServerSocketChannelAndServerContext = new HashMap<ServerSocketChannel, ServerContext>();
public static Map<SocketChannel, ChannelContext> getMapOfSocketChannelAndChannelContext()
{
return mapOfSocketChannelAndChannelContext;
}
/**
*
* @param selector
*/
public TcpListener(Selector selector, SynThreadPoolExecutor<SynRunnableIntf> synThreadPoolExecutor)
{
this.selector = selector;
this.synThreadPoolExecutor = synThreadPoolExecutor;
ThreadPoolMonitor.getInstance().register(synThreadPoolExecutor);
}
/**
* 接收消息,将消息入队列
*
* @param key
* @return
*/
public void read(SelectionKey key)
{
SocketChannel sc = (SocketChannel) key.channel();
ChannelContext channelContext = mapOfSocketChannelAndChannelContext.get(sc);
if (channelContext == null)
{
log.error("channelContext is null");
try
{
sc.close();
this.selector.wakeup();
} catch (IOException ioe)
{
log.error("channelContext is null,IOException occured when close the SocketChannel", ioe);
}
return;
}
try
{
ByteBuf datas = ChannelReader.read(sc, channelContext);// (key);
if (datas != null && datas.capacity() > 0)
{
submitMsg(datas, channelContext);
}
} catch (IOException e)
{
channelContext.getReadIOErrorHandler().handle(sc, e, channelContext, null);
return;
}
}
/**
* 提交原始消息给组包线程
*
* @param datas
* @param channelContext
* @return
*/
public boolean submitMsg(ByteBuf datas, ChannelContext channelContext)
{
DecodeRunnable packetOgnzerRunnable = channelContext.getDecodeRunnable();
if (packetOgnzerRunnable == null)
{
log.error("socketMsgProcessRunnable for {} is null", channelContext.getId());
return false;
}
if (packetOgnzerRunnable.getMsgQueue().size() > Config.getInstance().getSleepSize())
{
try
{
log.warn("there are {} msgs waiting for processing", packetOgnzerRunnable.getMsgQueue().size());
Thread.sleep(Config.getInstance().getSleepTime());
} catch (InterruptedException e)
{
log.error("", e);
}
}
packetOgnzerRunnable.addMsg(datas);
synThreadPoolExecutor.execute(packetOgnzerRunnable);
return true;
}
/**
* 在某端口接受client连接
*
* @param bindIp
* @param bindPort
* @throws IOException
*/
public void acceptAt(ServerContext serverContext) throws IOException
{
if (serverContext.getBindPort() <= 0)
{
throw new RuntimeException("port can not less than 0");
}
ServerSocketChannel socketChannel = ServerSocketChannel.open();
InetSocketAddress inetSocketAddress = null;
if (serverContext.getBindIp() == null || "".equals(serverContext.getBindIp()))
{
inetSocketAddress = new InetSocketAddress(serverContext.getBindPort());
// serverContext.setBindIp(inetSocketAddress.getAddress().getHostAddress());
// serverContext.setBindPort(inetSocketAddress.getPort());
} else
{
inetSocketAddress = new InetSocketAddress(serverContext.getBindIp(), serverContext.getBindPort());
}
ServerSocket socket = socketChannel.socket();
socket.bind(inetSocketAddress);
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_ACCEPT, this);
selector.wakeup();
mapOfServerSocketChannelAndServerContext.put(socketChannel, serverContext);
}
/**
* 监听消息,如果协议有消息发送过来,则处理消息(消息数据的接收是阻塞的,数据接收后,处理数据的过程是用线程完成的)
*/
public void listen()
{
new Thread(this.getClass().getSimpleName() + ":receive msg thread")
{
int c = 0;
@Override
public void run()
{
while (true)
{
try
{
if (selector.select(5000) <= 0)
{
if (log.isDebugEnabled())
{
log.debug("selector select(): nothing coming! count:" + c + ". selector.keys().size():"
+ selector.keys().size() + " " + selector.keys());
}
if (selector.keys().size() == 0)
{
Thread.sleep(1000);
}
c++;
if (c > 1)
{
c = 0;
try
{
selector.wakeup();
} catch (Exception e)
{
log.error("exception occured when selector wakeup", e);
}
}
continue;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> it = readyKeys.iterator();
while (it.hasNext())
{
SelectionKey key = null;
try
{
key = it.next();
it.remove(); // 此行很重要,容易被忘记
if (key.isValid() && key.isAcceptable()) // 作为服务器端时用到的
{
SocketChannel socketChannel = null;
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
try
{
ServerContext serverContext = mapOfServerSocketChannelAndServerContext
.get(serverSocketChannel);
socketChannel = serverSocketChannel.accept();
if (socketChannel == null)
{
return;
}
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketChannel
.getRemoteAddress();
InetSocketAddress localAddress = (InetSocketAddress) socketChannel
.getLocalAddress();
socketChannel.configureBlocking(false);
RemoteNode remoteNode = new RemoteNode(inetSocketAddress.getAddress()
.getHostAddress(), inetSocketAddress.getPort());
DecoderIntf packetOgnzerIntf = serverContext.getPacketOgnzerClass()
.newInstance();
PacketHandlerIntf packetHandler = serverContext.getPacketHandlerClass()
.newInstance();
ChannelContext channelContext = new ChannelContext(localAddress.getHostName(),
localAddress.getPort(), remoteNode, serverContext.getProtocol(),
packetOgnzerIntf, packetHandler);
if (serverContext.getChannelContextCompleter() != null)
{
serverContext.getChannelContextCompleter().complete(channelContext);
}
mapOfSocketChannelAndChannelContext.put(socketChannel, channelContext);
channelContext.setSocketChannel(socketChannel);
channelContext.getStatVo().setStateTimeTcpBuilding(SystemTimer.currentTimeMillis());
channelContext.setDesc4Err("");
Socket socket = socketChannel.socket();
socket.setSendBufferSize(1048576);
socket.setReceiveBufferSize(1048576);
socketChannel.register(selector, SelectionKey.OP_READ, TcpListener.this);
TcpListener.this.selector.wakeup();
log.info("{} socket connection has been built, waiting app connection ",
channelContext.getId());
channelContext.setConnectionState(ConnectionState.TCP_ON);
channelContext.getStatVo().setStateTimeAppBuilding(SystemTimer.currentTimeMillis());
channelContext.setAutoConnection(false);
channelContext
.setReadIOErrorHandler(com.talent.nio.handler.error.server.ReadIOErrorHandler
.getInstance());
channelContext
.setWriteIOErrorHandler(com.talent.nio.handler.error.server.WriteIOErrorHandler
.getInstance());
channelContext
.setErrorPackageHandler(com.talent.nio.handler.error.server.DefaultErrorPackageHandler
.getInstance());
ConnectionManager.getInstance().addConnection(channelContext);
} catch (IOException e)
{
try
{
socketChannel.close();
} catch (IOException e1)
{
log.error(e1.getMessage(), e1);
}
}
} else if (key.isValid() && key.isReadable())
{ // 只处理可读的情况
TcpListener socketMsgListener = (TcpListener) key.attachment();
if (socketMsgListener != null)
{
socketMsgListener.read(key);
} else
{
log.error("key.attachment() is null");
}
} else
{
// 正常情况到不了此处
log.error(
"key.isValid:{}, key.isAcceptable:{}, key.isConnectable:{}, key.isReadable:{}, key.isWritable{}",
key.isValid(), key.isAcceptable(), key.isConnectable(), key.isReadable(),
key.isWritable());
}
} catch (Exception e)
{
log.error(e + " Exception in loop of while(it.hasNext())", e); // 不处理这里面的异常,只是打印
}
}
} catch (IOException e)
{
log.error(e + " IOException occured when selector.select()");
try
{
Thread.sleep(100);
} catch (InterruptedException e1)
{
log.error("InterruptedException occured when sleep in selector.select()", e1);
}
} catch (Exception e)
{
log.error(e + "Exception occured when selector.select()--while(true)");
}
}
}
}.start();
}
// private static ChannelContext
// contain(Collection<ChannelContext> channelContexts, String
// remoteIp, int remotePort)
// {
// if (channelContexts == null)
// {
// return null;
// }
//
// for (ChannelContext channelContext : channelContexts)
// {
// if (remoteIp.equals(channelContext.getRemoteNode().getIp()) &&
// channelContext.getRemoteNode().getPort() == remotePort)
// {
// return channelContext;
// }
// }
// return null;
// }
// /**
// * 注册要监听的地址
// *
// * @param ip
// * 要监听的ip
// * @param port
// * 要监听的port
// * @param proxy
// * 代理地址,如果不用代理则传null
// * @return
// */
// public void register(String ip, int port, ChannelContext
// channelContext, String socketChannelId) throws IOException
// {
// InetSocketAddress address = new InetSocketAddress(ip, port);
// register(address, channelContext);
// }
/**
* 注册要监听的地址
*
* @param socketAddress
* 要监听的SocketAddress
* @param proxy
* 代理地址,如果不用代理则传null
* @return
* @throws IOException
*/
public void register(InetSocketAddress socketAddress, ChannelContext channelContext) throws IOException
{
try
{
Thread thread = new Thread(new BuildLinkThread(socketAddress, this, channelContext,
channelContext.getProxy()));
thread.setName("build link [" + channelContext.getId() + "]");
log.info(channelContext.getId() + " start thread for building link!");
thread.start();
channelContext.setConnectionState(ConnectionState.TCP_BUILDING);
channelContext.getStatVo().setStateTimeTcpBuilding(SystemTimer.currentTimeMillis());
channelContext.getStatVo().setCurrentReceivedTime(SystemTimer.currentTimeMillis());
} catch (Throwable e)
{
log.error("Exception occured when start building link thread", e);
}
}
/**
*
* @author 谭耀武
* @date 2012-08-09
*
*/
private static class BuildLinkThread implements Runnable
{
private InetSocketAddress socketAddress = null;
private TcpListener socketMsgListener = null;
private ChannelContext channelContext = null;
/**
*
* @param socketAddress
* @param socketMsgListener
* @param channelContext
* @param socketChannelId
* @param proxy
* 如果没有代理就传null
*/
BuildLinkThread(InetSocketAddress socketAddress, TcpListener socketMsgListener, ChannelContext channelContext,
Proxy proxy)
{
this.socketAddress = socketAddress;
this.socketMsgListener = socketMsgListener;
this.channelContext = channelContext;
}
@Override
public void run()
{
synchronized (channelContext)
{
try
{
log.debug("start buildlink for {}", channelContext.getId());
SocketChannel socketChannel = SocketChannel.open();
// String xx = socketChannel.toString();
bind(channelContext, socketChannel); // 绑定ip
if (channelContext.getProxy() == null)
{
try
{
socketChannel.connect(socketAddress);
} catch (IOException e)
{
log.error(
channelContext.getBindIp() + ":" + channelContext.getBindPort() + "---"
+ e.getLocalizedMessage(), e);
socketChannel.close();
throw e;
}
socketChannel.configureBlocking(false); // 非阻塞,此行不能少,否则IllegalBlockingModeException
} else
// 使用代理
{
socketChannel.connect(channelContext.getProxy().address());
socketChannel.configureBlocking(false); // 非阻塞,此行不能少,否则IllegalBlockingModeException
NioProxy.proxyImpl(socketChannel, socketAddress);
}
Socket socket = socketChannel.socket();
socket.setSendBufferSize(1048576); // 262142
socket.setReceiveBufferSize(1048576);
channelContext.setMyIp(socketChannel.socket().getLocalAddress().getHostAddress());
channelContext.setMyPort(socketChannel.socket().getLocalPort());
channelContext.generateId();
// int logIndex = 1;
// log.warn("" + logIndex++);
// CommunicateManager.getMapOfSocketChannelAndsocketChannelId().put(socketChannel,
// channelContext.getsocketChannelId());
mapOfSocketChannelAndChannelContext.put(socketChannel, channelContext);
channelContext.setSocketChannel(socketChannel);
channelContext.getStatVo().setStateTimeTcpBuilding(SystemTimer.currentTimeMillis());
channelContext.setDesc4Err("");
SendUtils.resumeCount(channelContext);
socketChannel.register(socketMsgListener.selector, SelectionKey.OP_READ, socketMsgListener); // 注册到selector中去
socketMsgListener.selector.wakeup();
log.info("{} socket connection has been built, waiting app connection ", channelContext.getId());
channelContext.setConnectionState(ConnectionState.TCP_ON);
} catch (Exception t)
{
log.error("occured when build link " + channelContext.getId(), t);
channelContext.getStatVo().getBuildExceptionTimes().incrementAndGet();
channelContext.setConnectionState(ConnectionState.TCP_LINKFAILED);
channelContext.setDesc4Err(t.getMessage());
} finally
{
// skip it
}
}
}
/**
*
* 绑定到指定的ip和port(服务器有时候会有多个ip,需要绑定指定的ip)
*
* @author tanyaowu
* @param channelContext
* @param socketChannel
*/
private static void bind(ChannelContext channelContext, SocketChannel socketChannel) throws Exception
{
if (channelContext.getBindIp() != null)
{
InetSocketAddress socketAddress = new InetSocketAddress(channelContext.getBindIp(),
channelContext.getBindPort());
socketChannel.socket().setReuseAddress(true);
socketChannel.socket().bind(socketAddress);
}
}
}
/**
* 注销对协议的接收监听
*
* @param socketChannelId
*/
public void logout(ChannelContext channelContext)
{
String socketChannelId = channelContext.getId();
if (channelContext != null)
{
log.debug("logout listen for [{}]", socketChannelId);
SocketChannel socketChannel = channelContext.getSocketChannel();
if (socketChannel == null)
{
log.error(socketChannelId + " socketChannel is null");
return;
}
try
{
if (socketChannel.isOpen())
{
socketChannel.close();
}
} catch (Exception e)
{
log.error(socketChannelId + " exception occured when close socketchannel", e);
}
try
{
this.selector.wakeup();
} catch (Exception e)
{
log.error(socketChannelId + " exception occured when selector.wakeup()", e);
}
}
}
public Selector getSelector()
{
return selector;
}
public void setThreadExecutor(SynThreadPoolExecutor<SynRunnableIntf> threadExecutor)
{
this.synThreadPoolExecutor = threadExecutor;
}
public SynThreadPoolExecutor<SynRunnableIntf> getThreadExecutor()
{
return synThreadPoolExecutor;
}
}