package org.voovan.network.udp;
import org.voovan.network.EventTrigger;
import org.voovan.network.MessageLoader;
import org.voovan.network.SocketContext;
import org.voovan.tools.TByteBuffer;
import org.voovan.tools.TObject;
import org.voovan.tools.log.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;
/**
* UDP事件监听器
*
* @author helyho
*
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class UdpSelector {
private Selector selector;
private SocketContext socketContext;
private UdpSession session;
/**
* 事件监听器构造
* @param selector 对象Selector
* @param socketContext socketContext 对象
*/
public UdpSelector(Selector selector, SocketContext socketContext) {
this.selector = selector;
this.socketContext = socketContext;
if (socketContext instanceof UdpSocket){
session = ((UdpSocket)socketContext).getSession();
}
}
/**
* 所有的事件均在这里触发
*/
public void eventChose() {
//读取用的缓冲区
ByteBuffer readTempBuffer = ByteBuffer.allocateDirect(socketContext.getBufferSize());
if (socketContext instanceof UdpSocket) {
// 连接完成onConnect事件触发
EventTrigger.fireConnectThread(session);
}
// 事件循环
try {
while (socketContext != null && socketContext.isOpen()) {
if (selector.select(1000) > 0) {
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> selectionKeyIterator = selectionKeys
.iterator();
while (selectionKeyIterator.hasNext()) {
SelectionKey selectionKey = selectionKeyIterator.next();
if (selectionKey.isValid()) {
// 获取 socket 通道
DatagramChannel datagramChannel = getDatagramChannel(selectionKey);
if (datagramChannel.isOpen() && selectionKey.isValid()) {
// 事件分发,包含时间 onRead onAccept
switch (selectionKey.readyOps()) {
// 有数据读取
case SelectionKey.OP_READ: {
int readSize = - 1;
UdpSocket clientUdpSocket = null;
UdpSession clientSession = session;
//接受的连接isConnected 是 false
//发起的连接isConnected 是 true
if(datagramChannel.isConnected()) {
readSize = datagramChannel.read(readTempBuffer);
}else{
SocketAddress address = datagramChannel.receive(readTempBuffer);
readSize = readTempBuffer.position();
clientUdpSocket = new UdpSocket(socketContext,(InetSocketAddress)address);
clientSession = clientUdpSocket.getSession();
}
//判断连接是否关闭
if (MessageLoader.isRemoteClosed(readTempBuffer, readSize) && clientSession.isConnected()) {
session.getMessageLoader().setStopType(MessageLoader.StopType.STREAM_END);
//如果 Socket 流达到结尾,则关闭连接
while(session.isConnected()) {
if (session.getByteBufferChannel().size() == 0) {
session.close();
}
}
break;
} else if (readSize > 0) {
readTempBuffer.flip();
clientSession.getByteBufferChannel().writeEnd(readTempBuffer);
readTempBuffer.clear();
}
readTempBuffer.clear();
// 触发 onRead 事件,如果正在处理 onRead 事件则本次事件触发忽略
EventTrigger.fireReceiveThread(clientSession);
break;
} default: {
Logger.debug("Nothing to do ,SelectionKey is:"
+ selectionKey.readyOps());
}
}
selectionKeyIterator.remove();
}
}
}
}
}
} catch (IOException e) {
// 触发 onException 事件
EventTrigger.fireExceptionThread(session, e);
}finally{
// 触发连接断开事件
if(session!=null) {
EventTrigger.fireDisconnect(session);
TByteBuffer.release(readTempBuffer);
}
}
}
/**
* 获取 socket 通道
*
* @param selectionKey 当前 Selectionkey
* @return SocketChannel 对象
* @throws IOException IO 异常
*/
public DatagramChannel getDatagramChannel(SelectionKey selectionKey)
throws IOException {
DatagramChannel datagramChannel = null;
// 取得通道
Object unknowChannel = selectionKey.channel();
if (unknowChannel instanceof DatagramChannel) {
datagramChannel = TObject.cast( unknowChannel );
}
return datagramChannel;
}
}