I have read this tutorial several times,
http://grexengine.com/sections/externalgames/articles/Adam%20Martin-Java%20NIO%20Networking%20for%20Games-2.html
as well as others, and came up with this, I would like you to tell me if I am doing anything wrong as well as answering some points I still don’t get (check comments).
What I think is having the User class hold the data (ArrayList TxMessages;) to be sent to that user. Using a Hasmap<SelectionKey, User> to find out who’s user the selectionkey belongs to I guess.
public class NIOClass {
public NIOCLass (int port) {
ConnSelector = Selector.open();
RxSelector = Selector.open();
TxSelector = Selector.open();
charset = Charset.forName("UTF-8");
TxPort = port;
RxPort = port;
init();
}
public void init() {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking( false );
ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress(RxPort);
ss.bind(address);
//SelectionKey key = ssc.register(RxSelector, SelectionKey.OP_READ);
//So I register the same channel + port with different selectors depending on their behavior
ssc.register(ConnSelector, SelectionKey.OP_ACCEPT);
ssc.register(RxSelector, SelectionKey.OP_READ);
ssc.register(TxSelector, SelectionKey.OP_WRITE);
}
//Serve for at least <minimum_time> as stop as soon as possible after serving for <maximum_time>
public void serve (int minimum_time, int maximum_time) {
long currentTime = System.currentTimeMillis();
long minTime = currentTime + minimum_time;
long maxTime = currentTime + maximum_time;
boolean keepGoing = true;
while (keepGoing) {
ConnSelector.selectNow();
Set keys = ConnSelector.selectedKeys();
if(keys.isEmpty()) {
TxSelector.selectNow();
keys = TxSelector.selectedKeys();
};
if(keys.isEmpty()) {
RxSelector.selectNow();
keys = RxSelector.selectedKeys();
}
Iterator it = keys.iterator();
while (keepGoing) {
if(it.hasNext()) {
SelectionKey key = (SelectionKey)it.next();
it.remove();
//processkey();
if(key.isAcceptable()) {
/* Banned IPs?
* Maximum # of connections per IP?
*/
ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true); // will this work or bring problems, what happens if two messages arrive for the same socket before I read.
//SelectionKey newKey = sc.register(RxSelector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
keys.add(sc.register(RxSelector, SelectionKey.OP_READ)); // keys.add(), is it necessary?, I saw it somewhere else.
sc.register(TxSelector, SelectionKey.OP_WRITE);
//and add the SelectionKeys to the respective User class
} else if(key.isWritable()) {
//get respective User class from a hashmap or whatever
String message;
//message = User.TxMessages.next();
ByteBuffer bb = charset.encode(message);
WritableByteChannel wbc = (WritableByteChannel)key.channel();
SocketChannel sc;
wbc.write(bb);
} else if(key.isReadable()) {
ReadableByteChannel rbc = (ReadableByteChannel) key.channel();
ByteBuffer bb = null;
bb = (ByteBuffer) key.attachment();
if(bb == null) {
// attach InputBuffer to key
bb = ByteBuffer.allocate(1024);
key.attach(bb);
} else {
// If we already had an inputBuffer, we need to make sure it's ready for use (what does this do?)
bb.limit(bb.capacity());
}
int numBytesRead = rbc.read(bb);
if(numBytesRead < 0) {
// This is an undocumented feature - it means the client has disconnected
key.cancel();
key.channel().close();
} else {
while( /*there_are_still_requests_in_the_buffer*/ true ) {
// Process the first request
// Remove the first request
}
}
rbc.read(bb);
String text = charset.decode(bb).toString();
//do something
}
};
currentTime = System.currentTimeMillis();
if(currentTime > minTime) keepGoing = false;
if(currentTime > maxTime) keepGoing = false;
}
}
return;
}
public void write(User user, String message) {
//User.TxMessages.add(message);
}
private int TxPort;
private int RxPort;
private java.nio.channels.Selector ConnSelector;
private java.nio.channels.Selector RxSelector;
private java.nio.channels.Selector TxSelector;
Charset charset;
}