package com.github.nettybook.ch0; import java.io.IOException; import java.net.InetAddress; 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.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; public class NonBlockingServer { private InetAddress addr; private int port; private Selector selector; private Map<SocketChannel, List<byte[]>> dataMap; public NonBlockingServer(InetAddress addr, int port) throws IOException { this.addr = addr; this.port = port; dataMap = new HashMap<SocketChannel, List<byte[]>>(); startServer(); } private void startServer() throws IOException { // create selector and channel this.selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); // bind to port InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port); serverChannel.socket().bind(listenAddr); serverChannel.register(this.selector, SelectionKey.OP_ACCEPT); // processing while (true) { // wait for events this.selector.select(); // wakeup to work on selected keys Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = (SelectionKey) keys.next(); // this is necessary to prevent the same key from coming up // again the next time around. keys.remove(); if (!key.isValid()) { continue; } if (key.isAcceptable()) { this.accept(key); } else if (key.isReadable()) { this.read(key); } else if (key.isWritable()) { this.write(key); } } } } private void accept(SelectionKey key) throws IOException { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel channel = serverChannel.accept(); channel.configureBlocking(false); // write welcome message channel.write(ByteBuffer.wrap("Welcome, this is the echo server\r\n".getBytes("US-ASCII"))); // register channel with selector for further IO dataMap.put(channel, new ArrayList<byte[]>()); channel.register(this.selector, SelectionKey.OP_READ); } private void read(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(8192); int numRead = -1; try { numRead = channel.read(buffer); } catch (IOException e) { e.printStackTrace(); } if (numRead == -1) { this.dataMap.remove(channel); channel.close(); key.cancel(); return; } byte[] data = new byte[numRead]; System.arraycopy(buffer.array(), 0, data, 0, numRead); // write back to client doEcho(key, data); } private void write(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); List<byte[]> pendingData = this.dataMap.get(channel); Iterator<byte[]> items = pendingData.iterator(); while (items.hasNext()) { byte[] item = items.next(); items.remove(); channel.write(ByteBuffer.wrap(item)); } key.interestOps(SelectionKey.OP_READ); } private void doEcho(SelectionKey key, byte[] data) { SocketChannel channel = (SocketChannel) key.channel(); List<byte[]> pendingData = this.dataMap.get(channel); pendingData.add(data); key.interestOps(SelectionKey.OP_WRITE); } public static void main(String[] args) throws Exception { new NonBlockingServer(null, 8888); } }