org.voovan.network.SocketSelector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of voovan-framework Show documentation
Show all versions of voovan-framework Show documentation
Voovan is a java framwork and it not depends on any third-party framework.
package org.voovan.network;
import org.voovan.Global;
import org.voovan.network.tcp.TcpServerSocket;
import org.voovan.network.udp.UdpServerSocket;
import org.voovan.network.udp.UdpSession;
import org.voovan.network.tcp.TcpSocket;
import org.voovan.network.udp.UdpSocket;
import org.voovan.tools.*;
import org.voovan.tools.buffer.ByteBufferChannel;
import org.voovan.tools.buffer.TByteBuffer;
import org.voovan.tools.collection.ArraySet;
import org.voovan.tools.hashwheeltimer.HashWheelTask;
import org.voovan.tools.log.Logger;
import org.voovan.tools.reflect.TReflect;
import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 选择器
*
* @author: helyho
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class SocketSelector implements Closeable {
private EventRunner eventRunner;
protected Selector selector;
protected ByteBuffer readTempBuffer;
protected ArraySet selectionKeys = new ArraySet(1024);
protected AtomicBoolean selecting = new AtomicBoolean(false);
private boolean useSelectNow = false;
/**
* 构造方法
* @param eventRunner 事件执行器
* @throws IOException IO 异常
*/
public SocketSelector(EventRunner eventRunner) throws IOException {
this.selector = SelectorProvider.provider().openSelector();
this.eventRunner = eventRunner;
readTempBuffer = TByteBuffer.allocateDirect();
try {
TReflect.setFieldValue(selector, NioUtil.selectedKeysField, selectionKeys);
TReflect.setFieldValue(selector, NioUtil.publicSelectedKeysField, selectionKeys);
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
//调试日志信息
if(Global.IS_DEBUG_MODE) {
Global.getHashWheelTimer().addTask(new HashWheelTask() {
@Override
public void run() {
System.out.print(eventRunner.getThread().getName() + " " + selector.keys().size() + " = " + eventRunner.getEventQueue().size());
int ioTaskCount = 0;
int eventTaskCount = 0;
int registerTaskCount = 0;
for (EventRunner.EventTask eventTask : eventRunner.getEventQueue()) {
if (eventTask.getPriority() == 4)
ioTaskCount++;
if (eventTask.getPriority() == 5)
eventTaskCount++;
if (eventTask.getPriority() == 6)
registerTaskCount++;
}
System.out.println(" (IO=" + ioTaskCount + ", Event=" + eventTaskCount + " ,register=" + registerTaskCount + ")");
}
}, 1);
}
}
public EventRunner getEventRunner() {
return eventRunner;
}
/**
* 注册一个 SocketContext 到选择器
* @param socketContext SocketContext 对象
* @param ops 需要关注的操作
* @return true:成功, false:失败
*/
public boolean register(SocketContext socketContext, int ops){
if(ops==0) {
IoSession session = socketContext.getSession();
session.setSocketSelector(this);
} else {
addChooseEvent(6, () -> {
try {
SelectionKey selectionKey = socketContext.socketChannel().register(selector, ops, socketContext);
if (socketContext.connectModel != ConnectModel.LISTENER) {
IoSession session = socketContext.getSession();
session.setSelectionKey(selectionKey);
session.setSocketSelector(this);
if (!session.isSSLMode()) {
EventTrigger.fireConnect(session);
} else {
//客户端模式主动发起 SSL 握手的第一个请求
session.getSSLParser().doHandShake();
}
}
socketContext.setRegister(true);
return true;
} catch (ClosedChannelException e) {
Logger.error("Register " + socketContext + " to selector error");
return false;
}
});
//正在 select 则唤醒
if (selecting.get()) {
selector.wakeup();
}
}
return true;
}
/**
* 在选择器中取消一个 SocketContext 的注册
* @param selectionKey SocketContext 对象
*/
public void unRegister(SelectionKey selectionKey) {
//===================================== 处理 selectionKey =====================================
try {
selectionKey.channel().close();
} catch (IOException e) {
e.printStackTrace();
}
if (selectionKey.isValid()) {
selectionKey.interestOps(0);
}
selectionKey.cancel();
//正在 select 则唤醒, 需要在 cancel 后立刻处理 selectNow
if (selecting.get()) {
selector.wakeup();
}
//===================================== 处理 SocketContext =====================================
SocketContext socketContext = (SocketContext) selectionKey.attachment();
if(socketContext!=null && socketContext.isRegister() && selectionKey.channel().isRegistered()) {
socketContext.setRegister(false);
selectionKey.attach(null);
socketContext.getSession().getReadByteBufferChannel().release();
socketContext.getSession().getSendByteBufferChannel().release();
if (socketContext.getSession().isSSLMode()) {
socketContext.getSession().getSSLParser().release();
}
EventTrigger.fireDisconnect(socketContext.getSession());
}
}
/**
* 是否在选择器绑定的执行器的线程中执行
* @return
*/
private boolean inEventRunner(){
return eventRunner.getThread() == Thread.currentThread();
}
/**
* 向执行器中增加一个选择事件
*/
public void addChooseEvent(){
addChooseEvent(4, null);
}
/**
* 向执行器中增加一个选择事件
* @param priority 指定的事件优先级
* @param supplier 在事件选择前执行的方法
*/
public void addChooseEvent(int priority, Callable supplier){
if(selector.isOpen()) {
eventRunner.addEvent(priority, () -> {
boolean result = true;
if(supplier!=null) {
try {
result = supplier.call();
} catch (Exception e) {
Logger.error("addChoseEvent error:",e);
result = false;
}
}
if(result) {
eventChoose();
}
});
}
}
int JvmEpollBugFlag = 0;
/**
* 事件选择业务累
*/
public void eventChoose() {
// 事件循环
try {
if (selector != null && selector.isOpen()) {
//执行选择操作
if(selector.keys().size() > 0) {
processSelect();
//如果有待处理的操作则下次调用 selectNow, 如果没有待处理的操作则调用带有阻赛的 select
if (!selectionKeys.isEmpty()) {
processSelectionKeys();
useSelectNow = true;
} else {
useSelectNow = false;
}
}
}
} catch (IOException e){
Logger.error("NioSelector error: ", e);
} finally {
if(inEventRunner() && !selector.keys().isEmpty()){
addChooseEvent();
}
}
}
/**
* 执行通道操作选择
* @throws IOException IO 异常
*/
private void processSelect() throws IOException {
if(useSelectNow){
selector.selectNow();
} else {
long dealTime = TEnv.measureTime(()->{
try {
selecting.compareAndSet(false, true);
selector.select(1000);
selecting.compareAndSet(true, false);
} catch (IOException e) {
e.printStackTrace();
}
});
int readyChannelCount = selectionKeys.size();
//Bug 探测
if(readyChannelCount ==0 && dealTime < 1000000*0.5){
JvmEpollBugFlag++;
} else {
JvmEpollBugFlag = 0;
}
if(JvmEpollBugFlag >=1024){
System.out.println(dealTime+ " detect bug on " + Thread.currentThread().getName());
}
}
}
/**
* 处理选择到的 Key
* @throws IOException IO 异常
*/
private void processSelectionKeys() throws IOException {
for (int i=0;i) socketContext, (DatagramChannel) channel);
}
socketContext.updateLastReadTime();
}
}
}
// else {
// unRegister(selectionKey);
// }
}
selectionKeys.reset();
}
/**
* 选择器关闭方法
*/
public void close() {
try {
TByteBuffer.release(readTempBuffer);
selector.close();
} catch (IOException e) {
Logger.error("close selector error");
}
}
/**
* 通用封装的从通道读数据的方法
* @param socketContext SocketContext 对象
* @param selectableChannel 读取的 SelectableChannel 对象
* @return 读取数据的字节数, -1:读取失败
*/
public int readFromChannel(SocketContext socketContext, SelectableChannel selectableChannel){
if(selectableChannel instanceof SocketChannel){
return tcpReadFromChannel((TcpSocket) socketContext, (SocketChannel)selectableChannel);
} else if(selectableChannel instanceof DatagramChannel){
return udpReadFromChannel((SocketContext)socketContext, (DatagramChannel) selectableChannel);
} else {
return -1;
}
}
/**
* 通用封装的向通道写数据的方法
* @param socketContext SocketContext 对象
* @param buffer 待写入的数据缓冲对象
* @return 写入数据的字节数, -1:写入失败
*/
public int writeToChannel(SocketContext socketContext, ByteBuffer buffer){
if(socketContext instanceof TcpSocket){
return tcpWriteToChannel((TcpSocket) socketContext, buffer);
} else if(socketContext instanceof UdpSocket){
return udpWriteToChannel((UdpSocket) socketContext, buffer);
} else {
return -1;
}
}
/**
* Tcp 服务接受一个新的连接
* @param socketContext SocketContext 对象
* @param socketChannel Socketchannel 对象
*/
public void tcpAccept(TcpServerSocket socketContext, SocketChannel socketChannel) {
try {
TcpSocket socket = new TcpSocket(socketContext, socketChannel);
EventTrigger.fireAccept(socket.getSession());
} catch(Exception e){
dealException(socketContext, e);
}
}
/**
* TCP 从通道读数据的方法
* @param socketContext TcpSocket 对象
* @param socketChannel 读取的 Socketchannel 对象
* @return 读取数据的字节数, -1:读取失败
*/
public int tcpReadFromChannel(TcpSocket socketContext, SocketChannel socketChannel) {
try {
int readSize = socketChannel.read(readTempBuffer);
readSize = loadAndPrepare(socketContext.getSession(), readSize);
return readSize;
} catch (Exception e) {
return dealException(socketContext, e);
}
}
/**
* TCP 向通道写数据的方法
* @param socketContext TcpSocket 对象
* @param buffer 待写入的数据缓冲对象
* @return 写入数据的字节数, -1:写入失败
*/
public int tcpWriteToChannel(TcpSocket socketContext, ByteBuffer buffer) {
try {
int totalSendByte = 0;
long start = System.currentTimeMillis();
if (socketContext.isConnected() && buffer != null) {
//循环发送直到全部内容发送完毕
while (socketContext.isConnected() && buffer.remaining() != 0) {
int sendSize = socketContext.socketChannel().write(buffer);
if (sendSize == 0) {
if (System.currentTimeMillis() - start >= socketContext.getSendTimeout()) {
Logger.error("SocketSelector tcpWriteToChannel timeout", new TimeoutException());
socketContext.close();
return -1;
}
} if (sendSize < 0){
socketContext.close();
return -1;
} else {
start = System.currentTimeMillis();
totalSendByte += sendSize;
}
}
}
return totalSendByte;
} catch (Exception e) {
return dealException(socketContext, e);
}
}
/**
* UDP 服务接受一个新的连接
* @param socketContext UdpServerSocket 对象
* @param datagramChannel DatagramChannel 对象
* @param address 接受通道的地址信息
* @return 接受收到的 UdpSocket 对象
* @throws IOException IO异常
*/
public UdpSocket udpAccept(UdpServerSocket socketContext, DatagramChannel datagramChannel, SocketAddress address) throws IOException {
UdpSocket udpSocket = new UdpSocket(socketContext, datagramChannel, (InetSocketAddress) address);
udpSocket.acceptStart();
return udpSocket;
}
/**
* UDP 从通道读数据的方法
* @param socketContext SocketContext 对象
* @param datagramChannel 读取的 DatagramChannel 对象
* @return 读取数据的字节数, -1:读取失败
*/
public int udpReadFromChannel(SocketContext socketContext, DatagramChannel datagramChannel) {
try {
int readSize = -1;
//接受的连接isConnected 是 false
//发起的连接isConnected 是 true
if (datagramChannel.isConnected()) {
readSize = datagramChannel.read(readTempBuffer);
} else {
socketContext = (UdpSocket) udpAccept((UdpServerSocket) socketContext, datagramChannel, datagramChannel.receive(readTempBuffer));
UdpSession session = socketContext.getSession();
readSize = readTempBuffer.position();
}
readSize = loadAndPrepare(socketContext.getSession(), readSize);
return readSize;
} catch (Exception e) {
return dealException(socketContext, e);
}
}
/**
* UDP 向通道写数据的方法
* @param socketContext UdpSocket 对象
* @param buffer 待写入的数据缓冲对象
* @return 写入数据的字节数, -1:写入失败
*/
public int udpWriteToChannel(UdpSocket socketContext, ByteBuffer buffer) {
try {
DatagramChannel datagramChannel = socketContext.socketChannel();
UdpSession session = socketContext.getSession();
int totalSendByte = 0;
long start = System.currentTimeMillis();
if (socketContext.isOpen() && buffer != null) {
//循环发送直到全部内容发送完毕
while (buffer.remaining() != 0) {
int sendSize = 0;
if (datagramChannel.isConnected()) {
sendSize = datagramChannel.write(buffer);
} else {
sendSize = datagramChannel.send(buffer, session.getInetSocketAddress());
}
if (sendSize == 0) {
TEnv.sleep(1);
if (System.currentTimeMillis() - start >= socketContext.getSendTimeout()) {
Logger.error("SocketSelector udpWriteToChannel timeout, Socket will be close");
socketContext.close();
return -1;
}
} else {
start = System.currentTimeMillis();
totalSendByte += sendSize;
}
}
}
return totalSendByte;
} catch (Exception e) {
return dealException(socketContext, e);
}
}
/**
* 数据读取
* @param session IoSession会话对象
* @param readSize 需要读取数据大小
* @return 实际读取的数据大小
* @throws IOException IO 异常
*/
public int loadAndPrepare(IoSession session, int readSize) throws IOException {
ByteBufferChannel appByteBufferChannel = session.getReadByteBufferChannel();
// 如果对端连接关闭,或者 session 关闭,则直接调用 session 的关闭
if (MessageLoader.isStreamEnd(readTempBuffer, readSize) || !session.isConnected()) {
session.getMessageLoader().setStopType(MessageLoader.StopType.STREAM_END);
session.close();
return -1;
} else {
readTempBuffer.flip();
if (readSize > 0) {
//如果缓冲队列已慢, 则等待可用, 超时时间为读超时
try {
TEnv.wait(session.socketContext().getReadTimeout(), () -> appByteBufferChannel.size() + readTempBuffer.limit() >= appByteBufferChannel.getMaxSize());
} catch (TimeoutException e) {
Logger.error("Session.readByteByteBuffer is not enough avaliable space:", e);
}
//如果在没有 SSL 支持 和 握手没有完成的情况下,直接写入
if (!SSLParser.isHandShakeDone(session)) {
session.getSSLParser().getSSlByteBufferChannel().writeEnd(readTempBuffer);
session.getSSLParser().doHandShake();
} else {
//接收SSL数据, SSL握手完成后解包
if (session.isSSLMode()) {
session.getSSLParser().unWarpByteBufferChannel(readTempBuffer);
} else {
appByteBufferChannel.writeEnd(readTempBuffer);
}
//检查心跳
if (session.getHeartBeat() != null) {
HeartBeat.interceptHeartBeat(session, appByteBufferChannel);
}
if (session.isConnected() && !session.getState().isReceive() && appByteBufferChannel.size() > 0) {
// 触发 onReceive 事件
EventTrigger.fireReceiveAsEvent(session);
}
}
// 接收完成后重置buffer对象
readTempBuffer.clear();
}
return readSize;
}
}
static String BROKEN_PIPE = "Broken pipe";
static String CONNECTION_RESET = "Connection reset by peer";
/**
* 异常处理方法
* @param socketContext SocketContext 对象
* @param e Exception 异常对象
* @return 永远返回 -1
*/
public int dealException(SocketContext socketContext, Exception e) {
if(BROKEN_PIPE.equals(e.getMessage()) || CONNECTION_RESET.equals(e.getMessage())){
socketContext.close();
return -1;
}
//兼容 windows 的 "java.io.IOException: 指定的网络名不再可用" 错误
if(e.getStackTrace()[0].getClassName().contains("sun.tcp.ch")){
return -1;
}
if(e instanceof Exception){
//触发 onException 事件
try {
EventTrigger.fireException((IoSession) socketContext.getSession(), e);
} catch (Exception ex) {
e.printStackTrace();
}
}
return -1;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy