Saturday, December 29, 2012

Simple Java NIO Socket Server

There are plenty of awesome Java Socket Server Implementations out there, the purpose of writing a rather simple implementation of my own is purely academic. Interested readers can check out,

  1. Netty at https://netty.io/
  2. Apache MINA at http://mina.apache.org/
The above implementations/frameworks have gone way past the point of being just "NIO Socket Servers". They are in fact fully evolved event based asynchronous systems you can use for a wide variety of needs.

Let's get down to business then, The following implementation simulates a request-reply.

The server simply listens for connections on a port and responds to valid requests with a reply.

package com.sockets.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Date;

public class ProcessSocketChannel implements Runnable {
 private SocketChannel socketChannel;
 private int BUFFER_SIZE = 1024;

 public ProcessSocketChannel(SocketChannel socketChannel) {
  this.socketChannel = socketChannel;
  Thread thread = new Thread(this);
  thread.start();
 }

 public void run() {
  System.out.println("Connection received from "
    + socketChannel.socket().getInetAddress().getHostAddress());
  readMessage();
  sendMessage("This is the server!!");
 }

 void sendMessage(String msg) {
  String fullmessage = new Date().toString() + " > " + msg;
  ByteBuffer buf = ByteBuffer.allocate(fullmessage.getBytes().length);
  buf.clear();
  buf.put(fullmessage.getBytes());
  buf.flip();
  while (buf.hasRemaining()) {
   try {
    socketChannel.write(buf);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

 void readMessage() {
  ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
  Charset charset = Charset.forName("us-ascii");
  CharsetDecoder decoder = charset.newDecoder();
  CharBuffer charBuffer;
  try {
   int bytes = socketChannel.read(byteBuffer);
   byteBuffer.flip();
   charBuffer = decoder.decode(byteBuffer);
   String result = charBuffer.toString();
   System.out.println(result);
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   byteBuffer = null;
   charset = null;
   decoder = null;
   charBuffer = null;
  }
 }
}

Processing of requests is delegated to a thread ProcessSocketChannel ,

package com.sockets.server;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Date;

public class ProcessSocketChannel implements Runnable {
 private SocketChannel socketChannel;
 private int BUFFER_SIZE = 1024;

 public ProcessSocketChannel(SocketChannel socketChannel) {
  this.socketChannel = socketChannel;
  Thread thread = new Thread(this);
  thread.start();
 }

 public void run() {
  System.out.println("Connection received from "
    + socketChannel.socket().getInetAddress().getHostAddress());
  readMessage();
  sendMessage("This is the server!!");
 }

 void sendMessage(String msg) {
  String fullmessage = new Date().toString() + " > " + msg;
  ByteBuffer buf = ByteBuffer.allocate(fullmessage.getBytes().length);
  buf.clear();
  buf.put(fullmessage.getBytes());
  buf.flip();
  while (buf.hasRemaining()) {
   try {
    socketChannel.write(buf);
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
 }

 void readMessage() {
  ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
  Charset charset = Charset.forName("us-ascii");
  CharsetDecoder decoder = charset.newDecoder();
  CharBuffer charBuffer;
  try {
   int bytes = socketChannel.read(byteBuffer);
   byteBuffer.flip();
   charBuffer = decoder.decode(byteBuffer);
   String result = charBuffer.toString();
   System.out.println(result);
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   byteBuffer = null;
   charset = null;
   decoder = null;
   charBuffer = null;
  }
 }
}

As for the client

package com.sockets.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Date;

public class Client {
 //The default buffer size
 private final int BUFFER_SIZE = 1024;
 private SocketChannel clientSocket;
 //The port of the server connecting to
 private int port = 7777;

 void run() {
  try {
   clientSocket = SocketChannel.open();
   //Obtaining the localhost
   InetAddress host = InetAddress.getLocalHost();
   //Connecting to server
   clientSocket.connect(new InetSocketAddress(host, port));
   System.out.println(String.format("Connected to %s on port %d",
     host.getHostAddress(), port));
   //Sending a message to the server
   sendMessage("Hello Server!!");
   //Reading the reply sent from the server
   readMessage();
  } catch (UnknownHostException unknownHost) {
   System.err.println("You are trying to connect to an unknown host!");
  } catch (IOException ioException) {
   ioException.printStackTrace();
  } finally {
   //Closing connection
   try {
    clientSocket.close();
   } catch (IOException ioException) {
    ioException.printStackTrace();
   }
  }
 }

 void sendMessage(String msg) throws IOException {
  String fullmessage = new Date().toString() + " > " + msg;

  ByteBuffer buf = ByteBuffer.allocate(fullmessage.getBytes().length);
  //Initialize the buffer
  buf.clear();
  buf.put(fullmessage.getBytes());
  //Flip the content of the buffer before writing
  buf.flip();
  //Writing Buffer Content to socket
  while (buf.hasRemaining()) {
   clientSocket.write(buf);
  }
 }

 void readMessage() {
  //Reads a text message
  ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE);
  Charset charset = Charset.forName("us-ascii");
  CharsetDecoder decoder = charset.newDecoder();
  CharBuffer charBuffer;
  try {
   int bytes = clientSocket.read(byteBuffer);
   byteBuffer.flip();
   charBuffer = decoder.decode(byteBuffer);
   String result = charBuffer.toString();
   System.out.println(result);
  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   byteBuffer = null;
   charset = null;
   decoder = null;
   charBuffer = null;
  }
 }

 public static void main(String args[]) {
  Client client = new Client();
  client.run();
 }
}

While I admit that the above implementation is less than ideal, given that it captures the basics of socket communications, I think it's a good starting point for those who still care about how the clock actually ticks.

2 comments:

  1. i hope u could talk about the advantage of using nio socketserver over io socketserver ...

    ReplyDelete
  2. I think you're missing part of the server code here (the main segment)! You posted package com.sockets.server twice!

    ReplyDelete