package com.github.nettybook.ch0;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
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 Main {
private Map<SocketChannel, List<byte[]>> keepDataTrack = new HashMap<>();
private ByteBuffer buffer = ByteBuffer.allocate(2 * 1024);
private void startEchoServer() {
// open Selector and ServerSocketChannel by calling the open() method
try (Selector selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
// check that both of them were successfully opened
if ((serverSocketChannel.isOpen()) && (selector.isOpen())) {
// configure non-blocking mode
serverSocketChannel.configureBlocking(false);
// set some options
serverSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 256 * 1024);
serverSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
// bind the server socket channel to port
serverSocketChannel.bind(new InetSocketAddress(8888));
// register the current channel with the given selector
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// display a waiting message while ... waiting!
System.out.println("Waiting for connections ...");
while (true) {
// wait for incomming events
selector.select();
// there is something to process on selected keys
Iterator keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = (SelectionKey) keys.next();
// prevent the same key from coming up again
keys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
acceptOP(key, selector);
}
else if (key.isReadable()) {
this.readOP(key);
}
else if (key.isWritable()) {
this.writeOP(key);
}
}
}
}
else {
System.out.println("The server socket channel or selector cannot be opened!");
}
}
catch (IOException ex) {
System.err.println(ex);
}
}
// isAcceptable returned true
private void acceptOP(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
System.out.println("Incoming connection from: " + socketChannel.getRemoteAddress());
// write an welcome message
socketChannel.write(ByteBuffer.wrap("Hello!\n".getBytes("UTF-8")));
// register channel with selector for further I/O
keepDataTrack.put(socketChannel, new ArrayList<byte[]>());
socketChannel.register(selector, SelectionKey.OP_READ);
}
// isReadable returned true
private void readOP(SelectionKey key) {
try {
SocketChannel socketChannel = (SocketChannel) key.channel();
buffer.clear();
int numRead = -1;
try {
numRead = socketChannel.read(buffer);
}
catch (IOException e) {
System.err.println("Cannot read error!");
}
if (numRead == -1) {
this.keepDataTrack.remove(socketChannel);
System.out.println("Connection closed by: " + socketChannel.getRemoteAddress());
socketChannel.close();
key.cancel();
return;
}
byte[] data = new byte[numRead];
System.arraycopy(buffer.array(), 0, data, 0, numRead);
System.out.println(new String(data, "UTF-8") + " from " + socketChannel.getRemoteAddress());
// write back to client
doEchoJob(key, data);
}
catch (IOException ex) {
System.err.println(ex);
}
}
// isWritable returned true
private void writeOP(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
List<byte[]> channelData = keepDataTrack.get(socketChannel);
Iterator<byte[]> its = channelData.iterator();
while (its.hasNext()) {
byte[] it = its.next();
its.remove();
socketChannel.write(ByteBuffer.wrap(it));
}
key.interestOps(SelectionKey.OP_READ);
}
private void doEchoJob(SelectionKey key, byte[] data) {
SocketChannel socketChannel = (SocketChannel) key.channel();
List<byte[]> channelData = keepDataTrack.get(socketChannel);
channelData.add(data);
key.interestOps(SelectionKey.OP_WRITE);
}
public static void main(String[] args) {
Main main = new Main();
main.startEchoServer();
}
}