/*
* 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.util;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.talent.nio.communicate.ChannelContext;
import com.talent.nio.communicate.ChannelContext.ConnectionState;
import com.talent.nio.communicate.monitor.vo.StatVo;
import com.talent.nio.communicate.receive.DecodeRunnable;
import com.talent.nio.communicate.receive.TcpListener;
import com.talent.nio.communicate.send.SendUtils;
import com.talent.nio.communicate.send.SendRunnable;
import com.talent.nio.connmgr.ConnectionManager;
import com.talent.nio.debug.DebugUtils;
import com.talent.nio.startup.Startup;
import com.talent.nio.utils.SystemTimer;
/**
*
*
* @author 谭耀武 2012-1-9 上午09:29:05
*
*/
public class NioUtils
{
private static final Logger log = LoggerFactory.getLogger(NioUtils.class);
/**
*
*/
private NioUtils()
{
}
/**
* 给SelectionKey添加监听选项
*
* @param key
* @param opt
*/
public static void addOpt(SelectionKey key, int opt)
{
if (key.isValid())
{
key.interestOps(key.interestOps() | opt);
}
}
/**
* 注销掉SelectionKey的监听选项
*
* @param key
* @param opt
*/
public static void removeOpt(SelectionKey key, int opt)
{
if (key.isValid())
{
key.interestOps(key.interestOps() & (~opt));
}
}
/**
* 略过通道的所有数据
*
* @param socketChannel
*/
public static int skipChannelData(ReadableByteChannel socketChannel) throws IOException
{
ByteBuffer buffer = ByteBuffer.allocate(1024);
int allLength = 0;
int length = 0;
while (socketChannel.isOpen() && (length = socketChannel.read(buffer)) > 0)
{
allLength += length;
buffer.clear();
}
log.debug("skipped length [{}]", allLength);
return allLength;
}
/**
* 统计发送流量
*
* @param length
* 消息长度
* @param channelContext
*/
public static void statSend(int length, ChannelContext channelContext)
{
long sentBytes = channelContext.getStatVo().getSentBytes() + length;
channelContext.getStatVo().setSentBytes(sentBytes);
log.debug("{} has sent {} bytes", channelContext.getId(), sentBytes);
}
/**
* 保存注销的时间
*/
private static Map<ChannelContext, Long> logoutTime = new HashMap<ChannelContext, Long>();
/**
* 断开连接
*
* @param channelContext
* @param reason
* @param isComplete
* 是否完全彻底的注销,true:彻底地断开(所保存的统计数据将全部丢失),移除连接时必须为true;
*/
private static boolean _disconnect(ChannelContext channelContext, String reason, boolean isComplete)
{
log.warn("start disconnect {}, reason:{}, isComplete:{}", channelContext.getId(), reason, isComplete);
SocketChannel socketChannel = channelContext.getSocketChannel();
if (ConnectionState.LOGOUTING == channelContext.getConnectionState())
{
// try
// {
// if (socketChannel.isOpen())
// {
// socketChannel.close();
// }
// } catch (Exception e)
// {
// log.error("exception occured when close socketchannel:" + channelContext, e);
// }
return false;
}
synchronized (channelContext)
{
// try
// {
// channelContext.setConnectionState(ConnectionState.LOGOUTING);
// if (socketChannel.isOpen())
// {
// socketChannel.close();
// }
// } catch (Exception e)
// {
// log.error("exception occured when close socketchannel:" + channelContext, e);
// }
//
// try
// {
// Startup.getSelector().wakeup();
// } catch (Exception e)
// {
// log.error("exception occured when selector.wakeup():" + channelContext, e);
// }
Long lastLogoutTime = logoutTime.get(channelContext);
long currTime = SystemTimer.currentTimeMillis();
if (lastLogoutTime != null)
{
long x = currTime - lastLogoutTime;
if ((x) < 100L)
{
log.error("注销得好频繁,上次注销在{}毫秒前, {} logouted at {}", x, channelContext.getId(), new Timestamp(
lastLogoutTime).toString());
return false;
}
}
if (DebugUtils.isNeedDebug(channelContext))
{
log.error("may be logout, reason {}, {}", reason, channelContext);
}
//
// if (ConnectionState.TCP_ON == channelContext.getConnectionState())
// {
// log.warn("can not logout, channelContext is TCP_ON. {}", channelContext);
// return false;
// }
logoutTime.put(channelContext, currTime);
try
{
// 注销通道的监听-- start
try
{
if (DebugUtils.isNeedDebug(channelContext))
{
log.warn("start logout, reason {}, {}", reason, channelContext.getId());
}
if (socketChannel != null)
{
// 注销通道的监听
TcpListener.getMapOfSocketChannelAndChannelContext().remove(socketChannel);
Startup.getTcpListener().logout(channelContext);
}
} catch (Exception e)
{
log.error("", e);
}
// 注销通道的监听-- end
// 清空socketChannelId对应的发送队列和发送线程工厂中的缓存对象 -- start
SendRunnable sendRunnable = channelContext.getSendRunnable();
if (sendRunnable != null)
{
sendRunnable.clearMsgQueue();
if (isComplete)
{
SendUtils.removeStat(channelContext);
} else
{
SendUtils.recordStat(channelContext, sendRunnable.getProcessedMsgCount(),
sendRunnable.getSubmitCount(),
sendRunnable.getProcessedMsgByteCount());
}
}
// 清空socketChannelId对应的发送队列和发送线程工厂中的缓存对象 -- end
// 清空socketChannelId对应的接收处理队列和接收处理线程工厂中的缓存对象 -- start
DecodeRunnable decodeRunnable = channelContext.getDecodeRunnable();
if (decodeRunnable != null)
{
decodeRunnable.clearMsgQueue();
if (isComplete)
{
StatUtils.removeStat(channelContext);
} else
{
StatUtils.recordStat(channelContext, decodeRunnable.getProcessedMsgCount(),
decodeRunnable.getSubmitCount(),
decodeRunnable.getProcessedMsgByteCount());
}
}
} catch (Throwable e)
{
log.error(" Throwable occured when logout " + channelContext.getId(), e);
channelContext.setDesc4Err("logout fail:" + e.getMessage());
} finally
{
if (channelContext != null)
{
channelContext.setConnectionState(ConnectionState.TCP_OFF);
channelContext.setDesc4Err(reason);
} else
{
log.warn(" channelContext is null");
}
if (DebugUtils.isNeedDebug(channelContext))
{
log.warn("end logout, reason {}, {}", reason, channelContext.getId());
}
Startup.getSelector().wakeup();
}
return true;
}
}
/**
* 删除连接
*
* @param _socketChannel
* @param channelContext
* @param reason
*/
public static void remove(ChannelContext channelContext, String reason)
{
boolean b = _disconnect(channelContext, reason, true);
if (b)
{
logoutTime.remove(channelContext);
ConnectionManager.getInstance().removeConnection(channelContext);
channelContext.setConnectionState(ConnectionState.REMOVED);
}
}
/**
* 断开连接
*
* @param channelContext
* @param reason
*/
public static void disconnect(ChannelContext channelContext, String reason)
{
_disconnect(channelContext, reason, false);
}
/**
* 将缓冲区的数据读到字节数组中
*
* @param buffer
* 不允许为null
* @param byteArray
* 不允许为null
*/
public static void readBufferToByteArray(ByteBuffer buffer, byte[] byteArray)
{
if (byteArray == null)
{
throw new RuntimeException("byteArray is null");
}
if (buffer == null)
{
throw new RuntimeException("buffer is null");
}
buffer.flip();
buffer.get(byteArray); // 将数据从buffer读到字节数组中
}
private static Set<ChannelContext.ConnectionState> canSendTcpMsgStates = new HashSet<ChannelContext.ConnectionState>();
static
{
canSendTcpMsgStates.add(ConnectionState.TCP_ON);
canSendTcpMsgStates.add(ConnectionState.APP_BUILDING);
canSendTcpMsgStates.add(ConnectionState.APP_ON);
canSendTcpMsgStates.add(ConnectionState.APP_OFF);
canSendTcpMsgStates.add(ConnectionState.APP_LINKFAILED);
}
public static boolean canSendTcpMsg(ChannelContext channelContext)
{
if (channelContext == null)
{
return false;
} else
{
return canSendTcpMsgStates.contains(channelContext.getConnectionState());
}
}
/**
*
* @param channelContext
* @return
*/
public static boolean buildLink(ChannelContext channelContext)
{
if (channelContext == null)
{
log.error("channelContext is null!");
return false;
}
final String ip = channelContext.getRemoteNode().getIp();
final int port = channelContext.getRemoteNode().getPort();
channelContext.getStatVo().getCountOfErrorPackage().set(0);// .setCountOfErrorPackage(0);
InetSocketAddress address = new InetSocketAddress(ip, port);
try
{
// 注册消息接收监听
Startup.getTcpListener().register(address, channelContext);
} catch (IOException e)
{
channelContext.getWriteIOErrorHandler().handle(null, e, channelContext,
"exception occured when build link!");
return false;
}
return true;
}
/**
* 创建统计信息
*
* @param channelContext
* @return
*/
public static StatVo createStatInfo(ChannelContext channelContext)
{
StatVo ret = channelContext.getStatVo();
int packetOgnzerQueueSize = channelContext.getDecodeRunnable().getMsgQueue().size();
int senderQueueSize = channelContext.getSendRunnable().getMsgQueue().size();
int packetHandlerQueueSize = channelContext.getHandlerRunnable().getMsgQueue().size();
ret.setPacketOgnzerQueueSize(packetOgnzerQueueSize);
ret.setSenderQueueSize(senderQueueSize);
ret.setPacketHandlerQueueSize(packetHandlerQueueSize);
return ret;
//
// int size1 =
// channelContext.getPacketOgnzerRunnable().getMsgQueue().size();
// int size2 =
// channelContext.getSocketMsgSendRunnable().getMsgQueue().size();
// int size3 =
// channelContext.getPacketHandlerRunnable().getMsgQueue().size();
// long size4 = channelContext.getSentBytes().longValue();
// long size5 = channelContext.getReceivedBytes().longValue();
//
// StringBuilder sb = new StringBuilder();
// sb.append(channelContext.getId()).append(StringUtil.NEWLINE);
// sb.append("PacketOgnzer Queue:").append(size1).append(StringUtil.NEWLINE);
// sb.append("Sender Queue:").append(size2).append(StringUtil.NEWLINE);
// sb.append("Handler Queue:").append(size3).append(StringUtil.NEWLINE);
// sb.append("SentBytes:").append(size4).append(StringUtil.NEWLINE);
// sb.append("ReceivedBytes:").append(size5).append(StringUtil.NEWLINE);
// return sb.toString();
}
/**
* @param args
*/
public static void main(String[] args) throws Exception
{
// Desktop.getDesktop().edit(new File("E:/work/2011-01/sss.dfda"));
// Desktop.getDesktop().open(new File("E:/work/2011-01/index.php.htm"));
Runtime.getRuntime().exec("cmd /c start E:/work/2011-01/index.php.htm");
}
}