package server.network.nio;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import server.utils.Log;
public class Server implements Runnable {
private ArrayList<ClientHandle> clients;
private Selector selector;
private Worker worker;
public Server(Worker worker) {
this.clients = new ArrayList<ClientHandle>();
this.worker = new Worker();
try {
this.start();
} catch (IOException e) {
Log.e("An error occurred when trying to start the server.", e,
this.getClass());
}
}
public void start() throws IOException {
Log.i("Server starting...", this.getClass());
// Instantiate a new selector
selector = Selector.open();
// Create server socket and channel
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(InetAddress
.getLocalHost().getHostAddress(), 1337);
ss.bind(address);
Log.i("Opening port " + address.getPort() + " on address "
+ address.getAddress().toString().split("/")[1],
this.getClass());
// Register channel with selector
ssc.register(selector, SelectionKey.OP_ACCEPT);
Log.i("Start sequence complete.", this.getClass());
}
@Override
public void run() {
Log.i("Server running.", this.getClass());
while (true) {
// Wait for new things to happen
try {
selector.select();
} catch (IOException e) {
Log.e("Could not select in server thread.", e, this.getClass());
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (!key.isValid()) {
Log.w("Invalid key selected: " + key.toString(), this.getClass());
continue;
}
if (key.isAcceptable()) {
ClientHandle c = this.accept(key);
it.remove();
if (c != null) {
clients.add(c);
Log.i("Client connected: " + c.id(), this.getClass());
}
} else if (key.isReadable()) {
this.read(key);
it.remove();
}
}
}
}
protected ClientHandle accept(SelectionKey key) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc;
try {
sc = ssc.accept();
sc.configureBlocking(false);
} catch (IOException e) {
Log.e("Could not accept socket connection.", e, this.getClass());
return null;
}
// Register the socket with the selector
SelectionKey k;
try {
k = sc.register(selector, SelectionKey.OP_READ);
} catch (ClosedChannelException e) {
Log.e(e, this.getClass());
return null;
}
// Add the client to the list of connected clients
return new ClientHandle(sc, k);
}
private void read(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int r;
try {
r = sc.read(readBuffer);
} catch (IOException e) {
// Remote end closed connection forcibly
closeConnection(key);
return;
}
if (r == -1) {
// Remote side closed connection cleanly
closeConnection(key);
return;
}
worker.processMessage(readBuffer.array(), r);
}
private void closeConnection(SelectionKey key) {
SocketChannel sc = (SocketChannel) key.channel();
try {
sc.close();
} catch (IOException e) {
Log.e("Error occurred when closing socket channel.", e,
this.getClass());
}
key.cancel();
}
public void stop() {
Log.i("Server stopping...", this.getClass());
Log.i("Server stopped.", this.getClass());
}
public static String getLocalIpAddress() {
InetAddress localaddr = null;
try {
localaddr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
Log.e("Could not get local IP.", e, Server.class);
}
return localaddr.toString().split("/")[1];
}
public static String getLocalExternalIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface ni = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = ni
.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) { // ignore 127.0.0.1
return inetAddress.getHostAddress().toString();
}
}
}
} catch (SocketException ex) {
}
return null;
}
}
package server.network.nio;
import java.util.LinkedList;
import java.util.Queue;
import server.utils.Log;
public class Worker implements Runnable {
private Queue<String> queue = new LinkedList<String>();
public void processMessage(byte[] data, int count) {
byte[] copiedData = new byte[count];
System.arraycopy(data, 0, copiedData, 0, count);
synchronized (queue) {
Log.d("Queue, in processMessage: " + System.identityHashCode(queue), this.getClass());
queue.add(new String(copiedData));
queue.notifyAll();
}
}
@Override
public void run() {
Log.d("Worker running.", this.getClass());
while (true) {
Log.d("Worker starting new loop...", this.getClass());
String msg;
// Wait for new data to become available
synchronized (queue) {
Log.d("Queue, in run: " + System.identityHashCode(queue), this.getClass());
while (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
// Swallow and wait some more...
}
}
msg = queue.poll();
}
Log.i("Processed message from client: " + msg, this.getClass());
}
}
}