Skip to content

Commit d3f2ea2

Browse files
committed
Work on iluwatar#74, enhanced reactor to allow multiple channels
1 parent 99adb5b commit d3f2ea2

File tree

3 files changed

+84
-35
lines changed

3 files changed

+84
-35
lines changed

reactor/src/main/java/com/iluwatar/reactor/App.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.iluwatar.reactor;
22

33
import java.io.IOException;
4+
import java.net.InetSocketAddress;
45
import java.nio.ByteBuffer;
6+
import java.nio.channels.DatagramChannel;
7+
import java.nio.channels.SelectableChannel;
8+
import java.nio.channels.ServerSocketChannel;
59
import java.nio.channels.SocketChannel;
610

711
import com.iluwatar.reactor.NioReactor.NioChannelEventHandler;
@@ -10,12 +14,35 @@ public class App {
1014

1115
public static void main(String[] args) {
1216
try {
13-
new NioReactor(6666, new LoggingServer()).start();
17+
NioReactor reactor = new NioReactor();
18+
19+
reactor
20+
.registerChannel(tcpChannel(6666))
21+
.registerChannel(tcpChannel(6667))
22+
.start();
23+
24+
reactor.registerHandler(new LoggingServer());
1425
} catch (IOException e) {
1526
e.printStackTrace();
1627
}
1728
}
18-
29+
30+
private static SelectableChannel udpChannel(int port) throws IOException {
31+
DatagramChannel channel = DatagramChannel.open();
32+
channel.socket().bind(new InetSocketAddress(port));
33+
channel.configureBlocking(false);
34+
System.out.println("Bound UDP socket at port: " + port);
35+
return channel;
36+
}
37+
38+
private static SelectableChannel tcpChannel(int port) throws IOException {
39+
ServerSocketChannel channel = ServerSocketChannel.open();
40+
channel.socket().bind(new InetSocketAddress(port));
41+
channel.configureBlocking(false);
42+
System.out.println("Bound TCP socket at port: " + port);
43+
return channel;
44+
}
45+
1946
static class LoggingServer implements NioChannelEventHandler {
2047

2148
@Override

reactor/src/main/java/com/iluwatar/reactor/AppClient.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
public class AppClient {
1010

1111
public static void main(String[] args) {
12-
new LoggingClient("Client 1", 6666).start();
12+
new Thread(new LoggingClient("Client 1", 6666)).start();
13+
new Thread(new LoggingClient("Client 2", 6667)).start();
1314
}
1415

1516

1617
/*
1718
* A logging client that sends logging requests to logging server
1819
*/
19-
static class LoggingClient {
20+
static class LoggingClient implements Runnable {
2021

2122
private int serverPort;
2223
private String clientName;
@@ -26,7 +27,7 @@ public LoggingClient(String clientName, int serverPort) {
2627
this.serverPort = serverPort;
2728
}
2829

29-
public void start() {
30+
public void run() {
3031
Socket socket = null;
3132
try {
3233
socket = new Socket(InetAddress.getLocalHost(), serverPort);
@@ -51,7 +52,7 @@ private void writeLogs(PrintWriter writer) {
5152
for (int i = 0; i < 10; i++) {
5253
writer.println(clientName + " - Log request: " + i);
5354
try {
54-
Thread.sleep(1000);
55+
Thread.sleep(100);
5556
} catch (InterruptedException e) {
5657
e.printStackTrace();
5758
}
Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,63 @@
11
package com.iluwatar.reactor;
22

33
import java.io.IOException;
4-
import java.net.InetSocketAddress;
4+
import java.nio.channels.SelectableChannel;
55
import java.nio.channels.SelectionKey;
66
import java.nio.channels.Selector;
77
import java.nio.channels.ServerSocketChannel;
88
import java.nio.channels.SocketChannel;
9+
import java.util.List;
910
import java.util.Set;
11+
import java.util.concurrent.CopyOnWriteArrayList;
1012

13+
/*
14+
* Abstractions
15+
* ---------------
16+
*
17+
* 1 - Dispatcher
18+
* 2 - Synchronous Event De-multiplexer
19+
* 3 - Event
20+
* 4 - Event Handler & concrete event handler (application business logic)
21+
* 5 - Selector
22+
*/
1123
public class NioReactor {
1224

13-
private int port;
1425
private Selector selector;
15-
private ServerSocketChannel serverSocketChannel;
16-
private NioChannelEventHandler nioEventhandler;
17-
18-
public NioReactor(int port, NioChannelEventHandler handler) {
19-
this.port = port;
20-
this.nioEventhandler = handler;
26+
private Acceptor acceptor;
27+
private List<NioChannelEventHandler> eventHandlers = new CopyOnWriteArrayList<>();
28+
29+
public NioReactor() throws IOException {
30+
this.acceptor = new Acceptor();
31+
this.selector = Selector.open();
2132
}
2233

34+
public NioReactor registerChannel(SelectableChannel channel) throws IOException {
35+
SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT);
36+
key.attach(acceptor);
37+
return this;
38+
}
39+
40+
public void registerHandler(NioChannelEventHandler handler) {
41+
eventHandlers.add(handler);
42+
}
2343

2444
public void start() throws IOException {
25-
startReactor();
26-
requestLoop();
45+
new Thread( new Runnable() {
46+
@Override
47+
public void run() {
48+
try {
49+
System.out.println("Reactor started, waiting for events...");
50+
eventLoop();
51+
} catch (IOException e) {
52+
e.printStackTrace();
53+
}
54+
}
55+
}).start();
2756
}
2857

29-
private void startReactor() throws IOException {
30-
selector = Selector.open();
31-
serverSocketChannel = ServerSocketChannel.open();
32-
serverSocketChannel.socket().bind(new InetSocketAddress(port));
33-
serverSocketChannel.configureBlocking(false);
34-
SelectionKey acceptorKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
35-
acceptorKey.attach(new Acceptor());
36-
System.out.println("Reactor started listening on port: " + port);
37-
}
38-
39-
private void requestLoop() throws IOException {
58+
private void eventLoop() throws IOException {
4059
while (true) {
41-
selector.select();
60+
selector.select(1000);
4261
Set<SelectionKey> keys = selector.selectedKeys();
4362
for (SelectionKey key : keys) {
4463
dispatchEvent(key);
@@ -50,21 +69,21 @@ private void requestLoop() throws IOException {
5069
private void dispatchEvent(SelectionKey key) throws IOException {
5170
Object handler = key.attachment();
5271
if (handler != null) {
53-
((EventHandler)handler).handle();
72+
((EventHandler)handler).handle(key.channel());
5473
}
5574
}
5675

5776
interface EventHandler {
58-
void handle() throws IOException;
77+
void handle(SelectableChannel channel) throws IOException;
5978
}
6079

6180
private class Acceptor implements EventHandler {
6281

63-
public void handle() throws IOException {
82+
public void handle(SelectableChannel channel) throws IOException {
6483
// non-blocking accept as acceptor will only be called when accept event is available
65-
SocketChannel clientChannel = serverSocketChannel.accept();
84+
SocketChannel clientChannel = ((ServerSocketChannel)channel).accept();
6685
if (clientChannel != null) {
67-
new ChannelHandler(clientChannel).handle();
86+
new ChannelHandler(clientChannel).handle(clientChannel);
6887
}
6988
System.out.println("Connection established with a client");
7089
}
@@ -88,9 +107,11 @@ public ChannelHandler(SocketChannel clientChannel) throws IOException {
88107
selector.wakeup();
89108
}
90109

91-
public void handle() throws IOException {
110+
public void handle(SelectableChannel channel) throws IOException {
92111
// only read events are supported.
93-
nioEventhandler.onReadable(clientChannel);
112+
for (NioChannelEventHandler eventHandler : eventHandlers) {
113+
eventHandler.onReadable(clientChannel);
114+
}
94115
}
95116
}
96117
}

0 commit comments

Comments
 (0)