package mypackage;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class MultiThreadedServer {
public static final int DEFAULT_FINISH_AWAIT_TIMEOUT = 5;
private ExecutorService threadPoolExecutor = Executors.newCachedThreadPool();
private ServerSocketChannel server;
private boolean isRunning;
private volatile int finishAwaitTimeout = DEFAULT_FINISH_AWAIT_TIMEOUT;
public MultiThreadedServer(int port) throws IOException {
server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(port));
server.configureBlocking(false);
}
public int getPort() {
return server.socket().getLocalPort();
}
public InetAddress getAddress() {
return server.socket().getInetAddress();
}
public boolean isRunning() {
return isRunning;
}
public synchronized void start() {
if (isRunning) return;
isRunning = true;
new Thread(new Runnable() {
@Override
public void run() {
try {
while (isRunning) {
SocketChannel client = server.accept();
if (client == null) continue;
System.out.println("Connection from " + client.socket().getInetAddress());
threadPoolExecutor.execute(new ConnectionProcessor(client));
}
} catch (IOException e) {
System.err.println("Error while accepting connections");
e.printStackTrace();
} finally {
System.out.println("Server is shutting down...");
System.out.println("Waiting remaining connection processors to terminate...");
try {
threadPoolExecutor.shutdown();
if(!threadPoolExecutor.awaitTermination(finishAwaitTimeout, TimeUnit.SECONDS)) {
System.err.println("Timeout has been exceeded\nForce terminating execution...");
threadPoolExecutor.shutdownNow();
if (!threadPoolExecutor.awaitTermination(finishAwaitTimeout, TimeUnit.SECONDS)) {
System.err.println("Pool has failed to terminate");
}
}
} catch (InterruptedException e) {
System.err.println("The threadPoolExecutor has been interrupted");
e.printStackTrace();
} finally {
try {
server.close();
} catch (IOException e) {
System.err.println("Error while closing the socket");
e.printStackTrace();
}
}
}
}
}, "The server's main thread").start();
System.out.println("The server is now running...");
}
public synchronized void stop() {
isRunning = false;
}
public int getFinishAwaitTimeout() {
return finishAwaitTimeout;
}
public void setFinishAwaitTimeout(int finishAwaitTimeout) {
this.finishAwaitTimeout = finishAwaitTimeout;
}
public static void main(String[] args) throws IOException {
final MultiThreadedServer server = new MultiThreadedServer(8080);
server.start();
for (int i = 0; i < 500; ++i) {
new Thread(new Runnable() {
@Override
public void run() {
try(SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(8080))) {
socketChannel.configureBlocking(false);
ByteBuffer buf = ByteBuffer.wrap("Hello from a client".getBytes());
while (buf.hasRemaining()) {
socketChannel.write(buf);
}
buf = ByteBuffer.allocate(128);
Charset charset = Charset.forName(System.getProperty("file.encoding"));
StringBuilder builder = new StringBuilder();
while (socketChannel.read(buf) != -1) {
buf.flip();
builder.append(charset.decode(buf));
buf.clear();
}
System.out.println("Server response: " + builder.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
new Thread(new Runnable() {
@Override
public void run() {
new Scanner(System.in).nextLine();
server.stop();
}
}).start();
}
}
class ConnectionProcessor implements Runnable {
private volatile static long numOfCons = 0;
private SocketChannel sock;
private long numOfCon;
public ConnectionProcessor(SocketChannel sock) {
this.sock = sock;
this.numOfCon = ++numOfCons;
}
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(128);
Charset charset = Charset.forName(System.getProperty("file.encoding"));
StringBuilder builder = new StringBuilder();
try {
sock.configureBlocking(false);
while(sock.read(buffer) > 0) {
buffer.flip();
builder.append(charset.decode(buffer));
buffer.clear();
}
System.out.printf("From client #%d:\n%s\n", numOfCon, builder.toString());
String respBody = "<html>" +
"<body>Hello, world</body>" +
"</html>";
String respHead = String.format("HTTP/1.1 200 OK\n" +
"Content-Type: text/html; charset=utf-8\n" +
"Content-Length: %d\n\n", respBody.getBytes().length);
buffer = ByteBuffer.wrap(respBody.getBytes());
ByteBuffer headBuffer = ByteBuffer.wrap(respHead.getBytes());
ByteBuffer[] bufAr = {headBuffer, buffer};
sock.write(bufAr);
} catch (IOException e) {
System.err.println("Error while processing the connection #" + numOfCon);
e.printStackTrace();
} finally {
try {
sock.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}