Skip to content

Commit ec8203a

Browse files
committed
Ongoing work on iluwatar#74 introduced better abstractions in reactor - tcp and udp mode
1 parent d3f2ea2 commit ec8203a

File tree

12 files changed

+424
-111
lines changed

12 files changed

+424
-111
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.iluwatar.reactor;
2+
3+
import java.io.IOException;
4+
import java.nio.ByteBuffer;
5+
import java.nio.channels.SelectableChannel;
6+
import java.nio.channels.SelectionKey;
7+
import java.util.Map;
8+
import java.util.Queue;
9+
import java.util.concurrent.ConcurrentHashMap;
10+
import java.util.concurrent.ConcurrentLinkedQueue;
11+
12+
public abstract class AbstractNioChannel {
13+
14+
private SelectableChannel channel;
15+
private ChannelHandler handler;
16+
private Map<SelectableChannel, Queue<ByteBuffer>> channelToPendingWrites = new ConcurrentHashMap<>();
17+
private NioReactor reactor;
18+
19+
public AbstractNioChannel(ChannelHandler handler, SelectableChannel channel) {
20+
this.handler = handler;
21+
this.channel = channel;
22+
}
23+
24+
public void setReactor(NioReactor reactor) {
25+
this.reactor = reactor;
26+
}
27+
28+
public SelectableChannel getChannel() {
29+
return channel;
30+
}
31+
32+
public abstract int getInterestedOps();
33+
34+
public abstract ByteBuffer read(SelectionKey key) throws IOException;
35+
36+
public void setHandler(ChannelHandler handler) {
37+
this.handler = handler;
38+
}
39+
40+
public ChannelHandler getHandler() {
41+
return handler;
42+
}
43+
44+
// Called from the context of reactor thread
45+
public void write(SelectionKey key) throws IOException {
46+
Queue<ByteBuffer> pendingWrites = channelToPendingWrites.get(key.channel());
47+
while (true) {
48+
ByteBuffer pendingWrite = pendingWrites.poll();
49+
if (pendingWrite == null) {
50+
System.out.println("No more pending writes");
51+
reactor.changeOps(key, SelectionKey.OP_READ);
52+
break;
53+
}
54+
55+
doWrite(pendingWrite, key);
56+
}
57+
}
58+
59+
protected abstract void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException;
60+
61+
public void write(ByteBuffer buffer, SelectionKey key) {
62+
Queue<ByteBuffer> pendingWrites = this.channelToPendingWrites.get(key.channel());
63+
if (pendingWrites == null) {
64+
synchronized (this.channelToPendingWrites) {
65+
pendingWrites = this.channelToPendingWrites.get(key.channel());
66+
if (pendingWrites == null) {
67+
pendingWrites = new ConcurrentLinkedQueue<>();
68+
this.channelToPendingWrites.put(key.channel(), pendingWrites);
69+
}
70+
}
71+
}
72+
pendingWrites.add(buffer);
73+
reactor.changeOps(key, SelectionKey.OP_WRITE);
74+
}
75+
}
Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,32 @@
11
package com.iluwatar.reactor;
22

33
import java.io.IOException;
4-
import java.net.InetSocketAddress;
5-
import java.nio.ByteBuffer;
6-
import java.nio.channels.DatagramChannel;
7-
import java.nio.channels.SelectableChannel;
8-
import java.nio.channels.ServerSocketChannel;
9-
import java.nio.channels.SocketChannel;
10-
11-
import com.iluwatar.reactor.NioReactor.NioChannelEventHandler;
124

135
public class App {
146

157
public static void main(String[] args) {
168
try {
17-
NioReactor reactor = new NioReactor();
18-
9+
NioReactor reactor = new NioReactor(new ThreadPoolDispatcher(2));
10+
LoggingHandler loggingHandler = new LoggingHandler();
1911
reactor
20-
.registerChannel(tcpChannel(6666))
21-
.registerChannel(tcpChannel(6667))
12+
.registerChannel(tcpChannel(6666, loggingHandler))
13+
.registerChannel(tcpChannel(6667, loggingHandler))
14+
.registerChannel(udpChannel(6668, loggingHandler))
2215
.start();
23-
24-
reactor.registerHandler(new LoggingServer());
2516
} catch (IOException e) {
2617
e.printStackTrace();
2718
}
2819
}
2920

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);
21+
private static AbstractNioChannel tcpChannel(int port, ChannelHandler handler) throws IOException {
22+
NioServerSocketChannel channel = new NioServerSocketChannel(port, handler);
23+
channel.bind();
3524
return channel;
3625
}
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);
26+
27+
private static AbstractNioChannel udpChannel(int port, ChannelHandler handler) throws IOException {
28+
NioDatagramChannel channel = new NioDatagramChannel(port, handler);
29+
channel.bind();
4330
return channel;
4431
}
45-
46-
static class LoggingServer implements NioChannelEventHandler {
47-
48-
@Override
49-
public void onReadable(SocketChannel channel) {
50-
ByteBuffer requestBuffer = ByteBuffer.allocate(1024);
51-
try {
52-
int byteCount = channel.read(requestBuffer);
53-
if (byteCount > 0) {
54-
byte[] logRequestContents = new byte[byteCount];
55-
byte[] array = requestBuffer.array();
56-
System.arraycopy(array, 0, logRequestContents, 0, byteCount);
57-
doLogging(new String(logRequestContents));
58-
}
59-
} catch (IOException e) {
60-
e.printStackTrace();
61-
}
62-
}
63-
64-
private void doLogging(String log) {
65-
// do logging at server side
66-
System.out.println(log);
67-
}
68-
}
6932
}

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

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

33
import java.io.IOException;
4+
import java.io.InputStream;
45
import java.io.OutputStream;
56
import java.io.PrintWriter;
7+
import java.net.DatagramPacket;
8+
import java.net.DatagramSocket;
69
import java.net.InetAddress;
10+
import java.net.InetSocketAddress;
711
import java.net.Socket;
12+
import java.net.SocketException;
813

914
public class AppClient {
1015

1116
public static void main(String[] args) {
12-
new Thread(new LoggingClient("Client 1", 6666)).start();
13-
new Thread(new LoggingClient("Client 2", 6667)).start();
17+
// new Thread(new LoggingClient("Client 1", 6666)).start();
18+
// new Thread(new LoggingClient("Client 2", 6667)).start();
19+
new Thread(new UDPLoggingClient(6668)).start();
1420
}
1521

1622

@@ -33,7 +39,7 @@ public void run() {
3339
socket = new Socket(InetAddress.getLocalHost(), serverPort);
3440
OutputStream outputStream = socket.getOutputStream();
3541
PrintWriter writer = new PrintWriter(outputStream);
36-
writeLogs(writer);
42+
writeLogs(writer, socket.getInputStream());
3743
} catch (IOException e) {
3844
e.printStackTrace();
3945
throw new RuntimeException(e);
@@ -48,15 +54,62 @@ public void run() {
4854
}
4955
}
5056

51-
private void writeLogs(PrintWriter writer) {
52-
for (int i = 0; i < 10; i++) {
57+
private void writeLogs(PrintWriter writer, InputStream inputStream) throws IOException {
58+
for (int i = 0; i < 1; i++) {
5359
writer.println(clientName + " - Log request: " + i);
5460
try {
5561
Thread.sleep(100);
5662
} catch (InterruptedException e) {
5763
e.printStackTrace();
5864
}
5965
writer.flush();
66+
byte[] data = new byte[1024];
67+
int read = inputStream.read(data, 0, data.length);
68+
if (read == 0) {
69+
System.out.println("Read zero bytes");
70+
} else {
71+
System.out.println(new String(data, 0, read));
72+
}
73+
}
74+
}
75+
}
76+
77+
static class UDPLoggingClient implements Runnable {
78+
private int port;
79+
80+
public UDPLoggingClient(int port) {
81+
this.port = port;
82+
}
83+
84+
@Override
85+
public void run() {
86+
DatagramSocket socket = null;
87+
try {
88+
socket = new DatagramSocket();
89+
for (int i = 0; i < 1; i++) {
90+
String message = "UDP Client" + " - Log request: " + i;
91+
try {
92+
DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, new InetSocketAddress(InetAddress.getLocalHost(), port));
93+
socket.send(packet);
94+
95+
byte[] data = new byte[1024];
96+
DatagramPacket reply = new DatagramPacket(data, data.length);
97+
socket.receive(reply);
98+
if (reply.getLength() == 0) {
99+
System.out.println("Read zero bytes");
100+
} else {
101+
System.out.println(new String(reply.getData(), 0, reply.getLength()));
102+
}
103+
} catch (IOException e) {
104+
e.printStackTrace();
105+
}
106+
}
107+
} catch (SocketException e1) {
108+
e1.printStackTrace();
109+
} finally {
110+
if (socket != null) {
111+
socket.close();
112+
}
60113
}
61114
}
62115
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.iluwatar.reactor;
2+
3+
import java.nio.ByteBuffer;
4+
import java.nio.channels.SelectionKey;
5+
6+
public interface ChannelHandler {
7+
8+
void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key);
9+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.iluwatar.reactor;
2+
3+
import java.nio.ByteBuffer;
4+
import java.nio.channels.SelectionKey;
5+
6+
public interface Dispatcher {
7+
void onChannelReadEvent(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key);
8+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.iluwatar.reactor;
2+
3+
import java.nio.ByteBuffer;
4+
import java.nio.channels.SelectionKey;
5+
6+
public class LoggingHandler implements ChannelHandler {
7+
8+
@Override
9+
public void handleChannelRead(AbstractNioChannel channel, ByteBuffer readBytes, SelectionKey key) {
10+
byte[] data = readBytes.array();
11+
doLogging(data);
12+
sendEchoReply(channel, data, key);
13+
}
14+
15+
private void sendEchoReply(AbstractNioChannel channel, byte[] data, SelectionKey key) {
16+
ByteBuffer buffer = ByteBuffer.wrap("Data logged successfully".getBytes());
17+
channel.write(buffer, key);
18+
}
19+
20+
private void doLogging(byte[] data) {
21+
// assuming UTF-8 :(
22+
System.out.println(new String(data));
23+
}
24+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.iluwatar.reactor;
2+
3+
import java.io.IOException;
4+
import java.net.InetAddress;
5+
import java.net.InetSocketAddress;
6+
import java.nio.ByteBuffer;
7+
import java.nio.channels.DatagramChannel;
8+
import java.nio.channels.SelectionKey;
9+
10+
public class NioDatagramChannel extends AbstractNioChannel {
11+
12+
private int port;
13+
14+
public NioDatagramChannel(int port, ChannelHandler handler) throws IOException {
15+
super(handler, DatagramChannel.open());
16+
this.port = port;
17+
}
18+
19+
@Override
20+
public int getInterestedOps() {
21+
return SelectionKey.OP_READ;
22+
}
23+
24+
@Override
25+
public ByteBuffer read(SelectionKey key) throws IOException {
26+
ByteBuffer buffer = ByteBuffer.allocate(1024);
27+
getChannel().receive(buffer);
28+
return buffer;
29+
}
30+
31+
@Override
32+
public DatagramChannel getChannel() {
33+
return (DatagramChannel) super.getChannel();
34+
}
35+
36+
public void bind() throws IOException {
37+
getChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
38+
getChannel().configureBlocking(false);
39+
System.out.println("Bound UDP socket at port: " + port);
40+
}
41+
42+
@Override
43+
protected void doWrite(ByteBuffer pendingWrite, SelectionKey key) throws IOException {
44+
pendingWrite.flip();
45+
getChannel().write(pendingWrite);
46+
}
47+
}

0 commit comments

Comments
 (0)