Skip to content

Instantly share code, notes, and snippets.

@edalorzo
Created July 27, 2020 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save edalorzo/39619f1ba09b4e05a7c57ba6e72fd0d9 to your computer and use it in GitHub Desktop.
Save edalorzo/39619f1ba09b4e05a7c57ba6e72fd0d9 to your computer and use it in GitHub Desktop.
package com.dalorzo.nio;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.*;
public class SelectedSockets {
private static int PORT_NUMBER = 1234;
public static void main(String[] args) throws Exception {
new SelectedSockets().go(args);
}
public void go(String[] argv) throws Exception {
int port = PORT_NUMBER;
if (argv.length > 0) {
port = Integer.parseInt(argv[0]);
}
System.out.println("Listening on port " + port);
//Allocate an unbound server socket channel
var serverChannel = ServerSocketChannel.open();
//Get the associated ServerSocket to bind it with
var serverSocket = serverChannel.socket();
//Create a new selector to use below
var selector = Selector.open();
//Set the port the sever channel will listen to
serverSocket.bind(new InetSocketAddress(port));
System.out.println("SO_RCVBUF: " + serverSocket.getOption(StandardSocketOptions.SO_RCVBUF));
System.out.println("SO_REUSEADDR: " + serverSocket.getOption(StandardSocketOptions.SO_REUSEADDR));
//Set nonblocking mode for the listening socket
serverChannel.configureBlocking(false);
//Registers the ServerChannel wit the selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
//This may block for a long time. Upon returning,
//the selected set contains key of the ready channels.
var n = selector.select();
if (n == 0) {
continue; //nothing to do
}
//Get an iterator over the set of selected keys
var it = selector.selectedKeys().iterator();
//Look at each key in the selected set
while (it.hasNext()) {
var key = it.next();
//Is a new connection coming in?
if(key.isAcceptable()){
var server = (ServerSocketChannel) key.channel();
var channel = server.accept();
registerChannel(selector, channel, SelectionKey.OP_READ);
sayHello(channel);
}
//Is there data to read on this channel?
if(key.isReadable()) {
readDataFromSocket(key);
}
it.remove();
}
}
}
private void registerChannel(Selector selector, SelectableChannel channel, int ops) throws Exception {
if(channel == null) {
return; //could happen
}
//Set the new channel to non-blocking
channel.configureBlocking(false);
//register it with the selector
channel.register(selector, ops);
}
private void sayHello(WritableByteChannel channel) throws Exception {
buffer.clear();
buffer.put("Hi there!\r\n".getBytes());
buffer.flip();
channel.write(buffer);
}
//Use the same byte buffer for all channels. A single thread is
//servicing all the channels, so no danger of concurrent access.
private ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
protected void readDataFromSocket(SelectionKey key) throws Exception {
var channel = (SocketChannel) key.channel();
buffer.clear(); //empty buffer
int count;
while((count = channel.read(buffer)) > 0) {
buffer.flip(); //make buffer readable
//Send data; don't assume it goes all at once
while(buffer.hasRemaining()) {
channel.write(buffer);
}
//WARNING: the above loop is evil. Because
//it's writing back to the same nonblocking
//channel it read the data from, this code
//can potentially spin in a busy loop. In real life
//you'd do something more useful than this.
buffer.clear(); //Empty buffer
}
if(count < 0) {
//Close channel on EOF, invalidates the key
channel.close();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment