Created
July 27, 2020 01:20
-
-
Save edalorzo/39619f1ba09b4e05a7c57ba6e72fd0d9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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