package net.yura.lobby.client;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Michael Chaten
*/
public abstract class SocketClient extends Thread {
public static final Logger logger = Logger.getLogger( LobbyCom.class.getName() );
private static final int HEADER_SIZE = 4; // a int to be sent as the size
private ByteBuffer headerBuffer;
private SocketChannel socketChannel;
private volatile Selector poll;
private boolean connected;
private String server;
private int port;
private InetAddress ipAddr;
public SocketClient() {
super(null,null,"LobbyCom -- Main Thread",100000000);
headerBuffer = ByteBuffer.allocateDirect(HEADER_SIZE);
}
public void connect(String sev, int p) {
server = sev;
port = p;
start();
}
public void disconnect() {
if (connected) {
try {
socketChannel.close();
// TODO, this is not even close to enough
}
catch (IOException e) { }
}
}
public void run() {
try {
ipAddr = InetAddress.getByName(server);
}
catch(Exception ex) {
// cant work out who to connect to, this is very bad!
throw new RuntimeException(ex);
}
// This will ALWAYS LOOP
while (true) {
try {
// make a connection
log("Trying to connect to: "+ipAddr+":"+port);
poll = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(ipAddr, port));
socketChannel.register(poll,SelectionKey.OP_CONNECT);
log("Pending Connection");
SocketChannel sock;
Iterator iterator;
// go into the loop of when we are connected
while (true) {
if (poll.select() != 0) {
iterator = poll.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
if (key.isConnectable()) {
// finish doing the connection
sock = (SocketChannel) key.channel();
boolean result = sock.finishConnect();
log("Finished Connecting "+result);
poll = Selector.open();
sock.register(poll, SelectionKey.OP_READ);
connected = true;
log("Initializing Handshake");
connected();
}
if (key.isReadable()) {
// we are connected, lets read data!
sock = (SocketChannel)key.channel();
try {
analyzeMessage(sock);
}
catch (IOException e) {
key.cancel();
throw e;
}
}
iterator.remove();
}
}
}
}
catch (IOException e) {
// if there is some IO Error we will log it, then try to reconnect
// currently this includes errors like the server not being available
logger.log(Level.INFO, "lobby com error", e);
connected = false;
disconnected();
// now will try and reconnect again
// sleep for a bit, not to hurt the server
try { Thread.sleep(1000); } catch(Exception ex) {}
}
}
}
public void sendToNetwork(byte[] bytes) {
try {
ByteBuffer[] writeBuffers = new ByteBuffer[]{ByteBuffer.allocate(HEADER_SIZE), null};
writeBuffers[0].putInt(bytes.length);
writeBuffers[0].flip();
writeBuffers[1] = ByteBuffer.wrap(bytes);
long bytesWritten = 0;
long bytesToWrite = writeBuffers[0].limit() + writeBuffers[1].limit();
while (bytesWritten < bytesToWrite) {
bytesWritten += socketChannel.write(writeBuffers);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
private void analyzeMessage(SocketChannel sc) throws IOException {
sc.read(headerBuffer);
if (headerBuffer.position() == 0) {
throw new IOException("Read 0 bytes");
}
headerBuffer.flip();
final int messageLen = headerBuffer.getInt();
headerBuffer.clear();
ByteBuffer messageBuffer = ByteBuffer.allocate(messageLen);
int read = 0;
while (read != messageLen) {
read += sc.read(messageBuffer);
}
messageBuffer.flip();
gotMessage( messageBuffer.array() );
}
abstract void log(String msg);
abstract void connected();
abstract void disconnected();
abstract void gotMessage(byte[] bytes);
}