package test.httpd; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; import java.util.Iterator; import java.util.Map; import java.util.concurrent.BlockingDeque; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.LinkedBlockingDeque; /** * @author Jon Brisbin <jon@jbrisbin.com> */ public class SimpleDisruptorNioHTTPD { final static BlockingDeque<ByteBuffer> bufferPool = new LinkedBlockingDeque<>(1024); final static Map<Long, SocketChannel> channels = new ConcurrentSkipListMap<>(); final static Map<Long, SelectionKey> selectionKeys = new ConcurrentSkipListMap<>(); static final ByteBuffer msg = ByteBuffer.wrap(("HTTP/1.1 200 OK\r\n" + "Connection: Keep-Alive\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 12\r\n\r\n" + "Hello World!").getBytes()); public static void onEvent(SelectionEvent ev) throws Exception { // Allocate a ByteBuffer from a RingBuffer ByteBuffer buffer = bufferPool.take(); if (buffer.position() > 0) { buffer.clear(); } SocketChannel channel = channels.get(ev.id); SelectionKey key = selectionKeys.get(ev.id); // System.out.println("ops:" + key.interestOps()); try { int read = safeRead(channel, buffer); while (read > 0) { safeWrite(channel, msg.duplicate()); // Read the data into memory buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); // String input = new String(bytes); buffer.clear(); read = safeRead(channel, buffer); } if (read < 0) { key.cancel(); channel.close(); selectionKeys.remove(ev.id); } } finally { // Put the ByteBuffer back into the RingBuffer for // re-use bufferPool.add(buffer); } } @SuppressWarnings("unused") public static void main(String[] args) throws Exception { for (int i = 0; i < 1024; i++) { bufferPool.add(ByteBuffer.allocate(1024)); } final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 3000), 1024); Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); Long workerId = 0L; while (true) { int cnt = 0; try { cnt = selector.select(); } catch (CancelledKeyException e) { } if (cnt > 0) { Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = keys.next(); keys.remove(); if (key.isValid()) { SelectionEvent event; if (key.isAcceptable()) { ServerSocket serverSocket = serverSocketChannel.socket(); // serverSocket.setReceiveBufferSize(bufferSize); // serverSocket.setReuseAddress(true); boolean hasSocket = true; do { SocketChannel channel = serverSocketChannel.accept(); if (null != channel) { channel.configureBlocking(false); // channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); // channel.setOption(StandardSocketOptions.TCP_NODELAY, true); // channel.setOption(StandardSocketOptions.SO_RCVBUF, bufferSize); // channel.setOption(StandardSocketOptions.SO_SNDBUF, bufferSize); SelectionKey readKey = channel.register(selector, SelectionKey.OP_READ); // // Allocate an Event object for dispatching // // to the handler // event = workerRing.get(workerId); // event.id = workerId; // channels.put(workerId, channel); // selectionKeys.put(workerId, readKey); // // Dispatch this event to a handler // workerRing.publish(workerId); // // Immediately allocate the next worker ID // workerId = workerRing.next(); } else { hasSocket = false; } } while (hasSocket); } else if (key.isReadable()) { event = new SelectionEvent(); event.id = workerId; SocketChannel channel = (SocketChannel) key.channel(); channels.put((Long)workerId, channel); selectionKeys.put(workerId, key); onEvent(event); } } } } } } static long cntIoError = 0; static long cntCancelledKeyException = 0; static int safeRead(ReadableByteChannel channel, ByteBuffer dst) throws IOException { int read = -1; try { // Read data from the Channel read = channel.read(dst); } catch (ClosedChannelException e) { } catch (IOException e) { switch ("" + e.getMessage()) { case "null": case "Connection reset by peer": case "Broken pipe": break; default: cntIoError++; System.out.println("error count IoError: " + cntIoError); } channel.close(); e.printStackTrace(); } catch (CancelledKeyException e) { cntCancelledKeyException++; System.out.println("error count CancelledKey: " + cntCancelledKeyException); channel.close(); } return read; } static int safeWrite(WritableByteChannel channel, ByteBuffer src) throws IOException { int written = -1; try { // Write the response immediately written = channel.write(src); } catch (IOException e) { switch ("" + e.getMessage()) { case "null": case "Connection reset by peer": case "Broken pipe": break; default: e.printStackTrace(); } channel.close(); } catch (CancelledKeyException e) { channel.close(); } return written; } static class SelectionEvent { Long id; public SelectionEvent() { } } }