package org.reunionemu.jreunion.network;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;
public abstract class NetworkThread<T extends Connection<T>> extends Thread {
private Selector selector;
public NetworkThread() throws IOException {
this.selector = Selector.open();
}
public SelectionKey bind(InetSocketAddress address) throws IOException{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(address);
synchronized(this){
selector.wakeup();
SelectionKey key = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
return key;
}
}
Selector getSelector() {
return selector;
}
public void onAccept(T connection){
throw new UnsupportedOperationException();
}
public void onDisconnect(T connection){
throw new UnsupportedOperationException();
}
public abstract T createConnection(SocketChannel socketChannel);
@Override
public void interrupt() {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
super.interrupt();
}
}
public T connect(InetSocketAddress address ) throws IOException{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
T connection = createConnection(socketChannel);
synchronized(this){
selector.wakeup();
SelectionKey key = socketChannel.register(selector, SelectionKey.OP_CONNECT);
key.attach(connection);
}
socketChannel.connect(address);
return connection;
}
public void onConnect(T connection){
throw new UnsupportedOperationException();
}
@Override
public void run() {
while(true) {
try {
int num = selector.select();
if(num == 0){
// we need synchronize here otherwise we might block again before we were able to change the selector
synchronized(this){
continue;
}
}
Set<SelectionKey> keys = selector.selectedKeys();
for(SelectionKey key: keys){
if(!key.isValid()){
continue;
}
boolean connectable = key.isConnectable(), readable = key.isReadable(), writable = key.isWritable(), acceptable = key.isAcceptable();
if (acceptable) {
SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();
T connection = createConnection(socketChannel);
connection.open();
onAccept(connection);
}
if(readable||writable||connectable){
T connection = (T) key.attachment();
if(connectable){
SocketChannel socketChannel = (SocketChannel)key.channel();
try {
if(socketChannel.finishConnect()){
connection.open();
}
onConnect(connection);
} catch(Exception e) {
e.printStackTrace();
}
}
if (writable) {
connection.handleOutput();
}
if (readable) {
connection.handleInput();
}
}
}
keys.clear();
} catch (Exception e) {
if(e instanceof ClosedSelectorException||e instanceof InterruptedException){
return;
}
e.printStackTrace();
}
}
}
}