Skip to content

Instantly share code, notes, and snippets.

@franzwong
Last active January 4, 2023 04:25
Show Gist options
  • Save franzwong/6cbf85ab77a279178e0d0e22f9ef2c37 to your computer and use it in GitHub Desktop.
Save franzwong/6cbf85ab77a279178e0d0e22f9ef2c37 to your computer and use it in GitHub Desktop.
Use Java Selector to handle multiple clients with single thread
package com.franzwong.selector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class Client {
private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
public static void main(String[] args) throws Exception {
try (var socket = SocketChannel.open(new InetSocketAddress("localhost", 8080))) {
var buffer = ByteBuffer.allocate(128);
byte[] remaining = new byte[Long.BYTES];
try {
boolean running = true;
while (running) {
socket.read(buffer);
buffer.flip();
int remainingBytes = buffer.remaining();
while (remainingBytes >= Long.BYTES) {
long counter = buffer.getLong();
LOGGER.info("Counter: {}", counter);
remainingBytes = buffer.remaining();
if (counter >= 1_000_000) {
running = false;
break;
}
}
if (remainingBytes > 0) {
buffer.get(remaining, 0, remainingBytes);
buffer.clear();
buffer.put(remaining, 0, remainingBytes);
} else {
buffer.clear();
}
}
} catch (Exception e) {
LOGGER.error("Error occurs", e);
}
}
}
}
package com.franzwong.selector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.atomic.LongAdder;
public class Server {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
private static class LongHolder {
long value = 0;
}
public static void main(String[] args) throws IOException {
int port = 8080;
try (var selector = Selector.open();
var serverSocket = ServerSocketChannel.open()) {
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
serverSocket.socket().bind(new InetSocketAddress(port));
var buffer = ByteBuffer.allocate(128);
while (true) {
selector.select();
var selectedKeys = selector.selectedKeys();
var iter = selectedKeys.iterator();
while (iter.hasNext()) {
var key = iter.next();
iter.remove(); // next selectedKeys call will return the same key if not removed
if (key.isAcceptable()) {
LOGGER.info("Client is connected");
var counter = new LongHolder();
var client = serverSocket.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE, counter);
}
if (key.isWritable()) {
var client = (SocketChannel) key.channel();
try {
var counter = (LongHolder) key.attachment();
counter.value++;
long counterValue = counter.value;
buffer.clear();
buffer.putLong(counterValue);
buffer.flip();
client.write(buffer);
if (counterValue >= 1_000_000L) {
LOGGER.info("Counter reaches 1,000,000");
client.close();
}
} catch (Exception e) {
LOGGER.error("Error occurs", e);
client.close();
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment