Im working on a socket listener that has to listen on 2 ports for 2 types of data( port 80 and port 81). These data are very similar as in the kind of operations that are perfor
NIO is called for when you need to scale to many thousands of simultaneous connections.
Otherwise, I'd suggest using multiple threads. For each port (and its corresponding ServerSocket
), create a thread that calls accept()
in a loop. These calls will block, but that is alright because other threads are running, taking care of any available tasks.
When a new Socket
is accepted, create another thread that is dedicated to that connection. It depends on the application, but typically this thread will read from the socket (a blocking operation), and perform the requested operation, writing the results back to the socket.
This architecture will scale to many hundreds of connections on most desktop platforms. And the programming model is fairly simple, as long as each connection is self-contained and independent of the others (this avoids concurrency issues). Introducing NIO will provide more scalability, but requires a lot of complexity.
Here a little example to get started with NIO.
It's a server listening on ports 80 and 81 and printing everything that is received on standard output. A connection is closed after receiving a packet starting with CLOSE
; the whole server is shutdown after receiving a packet starting with QUIT
. Missing the sending part and error handling could be a bit better. :-)
public static void main() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
ServerSocketChannel server1 = ServerSocketChannel.open();
server1.configureBlocking(false);
server1.socket().bind(new InetSocketAddress(80));
server1.register(selector, OP_ACCEPT);
ServerSocketChannel server2 = ServerSocketChannel.open();
server2.configureBlocking(false);
server2.socket().bind(new InetSocketAddress(81));
server2.register(selector, OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SocketChannel client;
SelectionKey key = iter.next();
iter.remove();
switch (key.readyOps()) {
case OP_ACCEPT:
client = ((ServerSocketChannel) key.channel()).accept();
client.configureBlocking(false);
client.register(selector, OP_READ);
break;
case OP_READ:
client = (SocketChannel) key.channel();
buffer.clear();
if (client.read(buffer) != -1) {
buffer.flip();
String line = new String(buffer.array(), buffer.position(), buffer.remaining());
System.out.println(line);
if (line.startsWith("CLOSE")) {
client.close();
} else if (line.startsWith("QUIT")) {
for (SelectionKey k : selector.keys()) {
k.cancel();
k.channel().close();
}
selector.close();
return;
}
} else {
key.cancel();
}
break;
default:
System.out.println("unhandled " + key.readyOps());
break;
}
}
}
}
Obs: the fields of SelectionKey
(OP_ACCEPT
...) are statically imported:
import static java.nio.channels.SelectionKey.*;
Many frameworks, such as Apache MINA and Netty, have been implemented based on Java NIO to boost non-blocking IO programming. I strongly recommend them to make your NIO programming a joy, rather than a nightmare. They fit your problem.
In addition, try to use an efficient protocol both in transport message size and encode/decode (serialize/deserialize) performance. Google Protocol Buffers is a reliable solution in this area. Also take a look at Kryo and KryoNet. They can be helpful.