Work on #74, enhanced reactor to allow multiple channels
This commit is contained in:
		| @@ -1,7 +1,11 @@ | ||||
| package com.iluwatar.reactor; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.nio.channels.DatagramChannel; | ||||
| import java.nio.channels.SelectableChannel; | ||||
| import java.nio.channels.ServerSocketChannel; | ||||
| import java.nio.channels.SocketChannel; | ||||
|  | ||||
| import com.iluwatar.reactor.NioReactor.NioChannelEventHandler; | ||||
| @@ -10,12 +14,35 @@ public class App { | ||||
|  | ||||
| 	public static void main(String[] args) { | ||||
| 		try { | ||||
| 			new NioReactor(6666, new LoggingServer()).start(); | ||||
| 			NioReactor reactor = new NioReactor(); | ||||
|  | ||||
| 			reactor | ||||
| 				.registerChannel(tcpChannel(6666)) | ||||
| 				.registerChannel(tcpChannel(6667)) | ||||
| 			.start(); | ||||
|  | ||||
| 			reactor.registerHandler(new LoggingServer()); | ||||
| 		} catch (IOException e) { | ||||
| 			e.printStackTrace(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	private static SelectableChannel udpChannel(int port) throws IOException { | ||||
| 		DatagramChannel channel = DatagramChannel.open(); | ||||
| 		channel.socket().bind(new InetSocketAddress(port)); | ||||
| 		channel.configureBlocking(false); | ||||
| 		System.out.println("Bound UDP socket at port: " + port); | ||||
| 		return channel; | ||||
| 	} | ||||
|  | ||||
| 	private static SelectableChannel tcpChannel(int port) throws IOException { | ||||
| 		ServerSocketChannel channel = ServerSocketChannel.open(); | ||||
| 		channel.socket().bind(new InetSocketAddress(port)); | ||||
| 		channel.configureBlocking(false); | ||||
| 		System.out.println("Bound TCP socket at port: " + port); | ||||
| 		return channel; | ||||
| 	} | ||||
|  | ||||
| 	static class LoggingServer implements NioChannelEventHandler { | ||||
|  | ||||
| 		@Override | ||||
|   | ||||
| @@ -9,14 +9,15 @@ import java.net.Socket; | ||||
| public class AppClient { | ||||
|  | ||||
| 	public static void main(String[] args) { | ||||
| 		new LoggingClient("Client 1", 6666).start(); | ||||
| 		new Thread(new LoggingClient("Client 1", 6666)).start(); | ||||
| 		new Thread(new LoggingClient("Client 2", 6667)).start(); | ||||
| 	} | ||||
|  | ||||
| 	 | ||||
| 	/* | ||||
| 	 * A logging client that sends logging requests to logging server | ||||
| 	 */ | ||||
| 	static class LoggingClient { | ||||
| 	static class LoggingClient implements Runnable { | ||||
|  | ||||
| 		private int serverPort; | ||||
| 		private String clientName; | ||||
| @@ -26,7 +27,7 @@ public class AppClient { | ||||
| 			this.serverPort = serverPort; | ||||
| 		} | ||||
|  | ||||
| 		public void start() { | ||||
| 		public void run() { | ||||
| 			Socket socket = null; | ||||
| 			try { | ||||
| 				socket = new Socket(InetAddress.getLocalHost(), serverPort); | ||||
| @@ -51,7 +52,7 @@ public class AppClient { | ||||
| 			for (int i = 0; i < 10; i++) { | ||||
| 				writer.println(clientName + " - Log request: " + i); | ||||
| 				try { | ||||
| 					Thread.sleep(1000); | ||||
| 					Thread.sleep(100); | ||||
| 				} catch (InterruptedException e) { | ||||
| 					e.printStackTrace(); | ||||
| 				} | ||||
|   | ||||
| @@ -1,44 +1,63 @@ | ||||
| package com.iluwatar.reactor; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.net.InetSocketAddress; | ||||
| import java.nio.channels.SelectableChannel; | ||||
| import java.nio.channels.SelectionKey; | ||||
| import java.nio.channels.Selector; | ||||
| import java.nio.channels.ServerSocketChannel; | ||||
| import java.nio.channels.SocketChannel; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||
|  | ||||
| /* | ||||
|  * Abstractions | ||||
|  * --------------- | ||||
|  *  | ||||
|  * 1 - Dispatcher | ||||
|  * 2 - Synchronous Event De-multiplexer | ||||
|  * 3 - Event | ||||
|  * 4 - Event Handler & concrete event handler (application business logic) | ||||
|  * 5 - Selector | ||||
|  */ | ||||
| public class NioReactor { | ||||
|  | ||||
| 	private int port; | ||||
| 	private Selector selector; | ||||
| 	private ServerSocketChannel serverSocketChannel; | ||||
| 	private NioChannelEventHandler nioEventhandler; | ||||
| 	private Acceptor acceptor; | ||||
| 	private List<NioChannelEventHandler> eventHandlers = new CopyOnWriteArrayList<>(); | ||||
| 	 | ||||
| 	public NioReactor(int port, NioChannelEventHandler handler) { | ||||
| 		this.port = port; | ||||
| 		this.nioEventhandler = handler; | ||||
| 	public NioReactor() throws IOException { | ||||
| 		this.acceptor = new Acceptor(); | ||||
| 		this.selector = Selector.open(); | ||||
| 	} | ||||
| 	 | ||||
| 	public NioReactor registerChannel(SelectableChannel channel) throws IOException { | ||||
| 		SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT); | ||||
| 		key.attach(acceptor); | ||||
| 		return this; | ||||
| 	} | ||||
| 	 | ||||
| 	public void registerHandler(NioChannelEventHandler handler) { | ||||
| 		eventHandlers.add(handler); | ||||
| 	} | ||||
| 	 | ||||
| 	public void start() throws IOException { | ||||
| 		startReactor(); | ||||
| 		requestLoop(); | ||||
| 		new Thread( new Runnable() { | ||||
| 			@Override | ||||
| 			public void run() { | ||||
| 				try { | ||||
| 					System.out.println("Reactor started, waiting for events..."); | ||||
| 					eventLoop(); | ||||
| 				} catch (IOException e) { | ||||
| 					e.printStackTrace(); | ||||
| 				} | ||||
| 			} | ||||
| 		}).start(); | ||||
| 	} | ||||
|  | ||||
| 	private void startReactor() throws IOException { | ||||
| 		selector = Selector.open(); | ||||
| 		serverSocketChannel = ServerSocketChannel.open(); | ||||
| 		serverSocketChannel.socket().bind(new InetSocketAddress(port)); | ||||
| 		serverSocketChannel.configureBlocking(false); | ||||
| 		SelectionKey acceptorKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); | ||||
| 		acceptorKey.attach(new Acceptor()); | ||||
| 		System.out.println("Reactor started listening on port: " + port); | ||||
| 	} | ||||
| 	 | ||||
| 	private void requestLoop() throws IOException { | ||||
| 	private void eventLoop() throws IOException { | ||||
| 		while (true) { | ||||
| 			selector.select(); | ||||
| 			selector.select(1000); | ||||
| 			Set<SelectionKey> keys = selector.selectedKeys(); | ||||
| 			for (SelectionKey key : keys) { | ||||
| 				dispatchEvent(key); | ||||
| @@ -50,21 +69,21 @@ public class NioReactor { | ||||
| 	private void dispatchEvent(SelectionKey key) throws IOException { | ||||
| 		Object handler = key.attachment(); | ||||
| 		if (handler != null) { | ||||
| 			((EventHandler)handler).handle(); | ||||
| 			((EventHandler)handler).handle(key.channel()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interface EventHandler { | ||||
| 		void handle() throws IOException; | ||||
| 		void handle(SelectableChannel channel) throws IOException; | ||||
| 	} | ||||
|  | ||||
| 	private class Acceptor implements EventHandler { | ||||
| 		 | ||||
| 		public void handle() throws IOException { | ||||
| 		public void handle(SelectableChannel channel) throws IOException { | ||||
| 			// non-blocking accept as acceptor will only be called when accept event is available | ||||
| 			SocketChannel clientChannel = serverSocketChannel.accept(); | ||||
| 			SocketChannel clientChannel = ((ServerSocketChannel)channel).accept(); | ||||
| 			if (clientChannel != null) { | ||||
| 				new ChannelHandler(clientChannel).handle(); | ||||
| 				new ChannelHandler(clientChannel).handle(clientChannel); | ||||
| 			} | ||||
| 			System.out.println("Connection established with a client"); | ||||
| 		} | ||||
| @@ -88,9 +107,11 @@ public class NioReactor { | ||||
| 			selector.wakeup(); | ||||
| 		} | ||||
|  | ||||
| 		public void handle() throws IOException { | ||||
| 		public void handle(SelectableChannel channel) throws IOException { | ||||
| 			// only read events are supported. | ||||
| 			nioEventhandler.onReadable(clientChannel); | ||||
| 			for (NioChannelEventHandler eventHandler : eventHandlers) { | ||||
| 				eventHandler.onReadable(clientChannel); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user