Friday 30 September 2016

Demonstration of Basic difference between blocking and non-blocking IO in java using Netty framework.

In this article I am going to talk very briefly about the key difference between blocking and non-blocking IO operation.

First I am going to show you the simple socket programming using traditional blocking IO (OIO) and after that I will show you the non-blocking IO (NIO) example using Netty framework.


1) Blocking IO client and server program

Server code:
 public class OioServer {   
   public static void main(String... args) throws IOException {   
    ServerSocket listener = new ServerSocket(6689);   
    try {   
     while (true) {   
      //listening for client connection   
      Socket socket = listener.accept();   
      try {   
   
       //reading stream received from client   
       BufferedReader clientDataStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));   
       String clientData = clientDataStream.readLine();   
       System.out.println("Client send :" + clientData);   
         
       //sending response to client        
       String response = clientData + " received by server";   
       PrintWriter out = new PrintWriter(socket.getOutputStream(), true);   
       out.println(response);   
      } finally {   
       socket.close();   
      }   
     }   
    } finally {   
     listener.close();   
    }   
   }   
  }   

Client code:
 public class OioClient {  
   public static void main(String... args) throws IOException {  
     while (true) {  
       BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
   
       //trying to connect to server   
       Socket s = new Socket("localhost", 6689);  
   
       //taking user input and sending it to server  
       PrintWriter out =  
           new PrintWriter(s.getOutputStream(), true);  
       System.out.print("Client input:");  
       out.println(br.readLine());  
   
       //reading back the server response   
       BufferedReader in = new BufferedReader(  
           new InputStreamReader(s.getInputStream()));  
       System.out.println(in.readLine());  
    }  
   
   }  
 }  


As you can see when I run this program it will run both client and server in a loop and keep taking input from client program and submitting it to the server while the server keep responding the client for each input received from client.Below is the output of server and client.

Client output:











Server output:







Lets make some changes to this code.
In the server code I am making below modifications.
I am adding 5 second sleep before server respond to client:
     //sending response to client    
     String response = clientData + " received by server";    
     PrintWriter out = new PrintWriter(socket.getOutputStream(), true);    
     try {    
     Thread.sleep(5000l);    
     } catch (InterruptedException e) {    
     e.printStackTrace();    
     }    
     out.println(response);  


In the client code I am making below changes:
Adding 2 sysout before and after fetching response from the server and calculating the delay in response.
     //reading back the server response   
     System.out.println("Waiting for server response...");   
     long startTime=System.nanoTime();   
     BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));   
     System.out.println(in.readLine());   
     System.out.println("Server response received...Time taken:"+(System.nanoTime()-startTime)/1000000000.0+" seconds");   


Lets run the program and see the difference:

Client output:








As you can judge from output that the in.readLine() method will block the thread and return only when the server response received.
For that 5 second duration client thread remain in blocked state just waiting for that IO to complete.


2) Non-Blocking IO client and server program using Netty

For those who are not familiar with Netty let me give you a brief introduction.

From the Netty web site:
"Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server."

It is a wrapper written over java NIO API for writing high performance client server application.
The next question comes into picture is what is java NIO ?? How it is different from the traditional IO(blocking IO). 

The answer of this question itself is a big discussion so I am giving you one of the best tutorial present on the web for NIO:

If you will go through this tutorial you can have basic idea about Channels, Buffers and Selectors.You will have understanding about how NIO is different from OIO by using Buffers to read from and write into rather than performing stream based OIO.
Netty is using java NIO API underneath and providing very powerful framework to write NIO based applications.
* You can also write OIO based application using Netty.


Server code:
 public class NioServer {   
     
   private static class MyServerChannelHandler extends SimpleChannelInboundHandler<String> {   
    @Override   
    public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {   
     System.out.println(msg + " received by server");   
     ctx.writeAndFlush("Hello from server\n");   
    }   
   }   
     
   public static void main(String... args) throws InterruptedException {   
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);   
    EventLoopGroup workerGroup = new NioEventLoopGroup(10);   
    try {   
     ServerBootstrap b = new ServerBootstrap();   
     b.group(bossGroup, workerGroup)   
       .channel(NioServerSocketChannel.class)   
       .childHandler(new ChannelInitializer<SocketChannel>() {   
        @Override   
        public void initChannel(SocketChannel ch) throws Exception {   
         ch.pipeline().addLast(new LineBasedFrameDecoder(100));   
         ch.pipeline().addLast(new StringDecoder());   
         ch.pipeline().addLast(new StringEncoder());   
     
         ch.pipeline().addLast(new MyServerChannelHandler());   
        }   
       })   
       .option(ChannelOption.SO_BACKLOG, 100)   
       .childOption(ChannelOption.SO_KEEPALIVE, true);   
     
     // Bind and start to accept incoming connections on port 8881.   
     ChannelFuture f = b.bind(8881).sync();   
     
     f.addListener(new GenericFutureListener<ChannelFuture>() {   
      public void operationComplete(ChannelFuture channelFuture) throws Exception {   
       if (channelFuture.isSuccess()) {   
        System.out.println("Server started...");   
       } else {   
        System.out.println(channelFuture.cause());   
       }   
      }   
     });   
     
     f.channel().closeFuture().sync();   
    } finally {   
     workerGroup.shutdownGracefully();   
     bossGroup.shutdownGracefully();   
    }   
     
   }   
  }    

Let me explain you what is happening in the above code by not going into much depth of syntactical part of Netty. We are exactly doing similar things which we did in OIO server program. We are trying to create a server socket on some port and listening on that for incoming connection of client and once client is connected we are responding it with "Hello from server\n". It is also sysout the message received from the client.

Client code:
 public class NioClient {  
   
   private static class MyClientChannelHandler extends SimpleChannelInboundHandler<String> {  
     @Override  
     public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {  
        System.out.println(msg);  
     }  
   }  
   
   public static void main(String... args) throws InterruptedException {  
     EventLoopGroup workerGroup = new NioEventLoopGroup();  
     try {  
       Bootstrap b = new Bootstrap();  
       b.group(workerGroup);  
       b.channel(NioSocketChannel.class);  
       b.option(ChannelOption.SO_KEEPALIVE, true);  
       b.handler(new ChannelInitializer<SocketChannel>() {  
         @Override  
         public void initChannel(SocketChannel ch) throws Exception {  
           ch.pipeline().addLast(new LineBasedFrameDecoder(100));  
           ch.pipeline().addLast(new StringDecoder());  
           ch.pipeline().addLast(new StringEncoder());  
           ch.pipeline().addLast(new MyClientChannelHandler());  
         }  
       });  
       // Start the client.  
       ChannelFuture f = b.connect("localhost", 8881).sync();  
   
       f.addListener(new GenericFutureListener<ChannelFuture>() {  
         public void operationComplete(ChannelFuture channelFuture) throws Exception {  
           if (channelFuture.isSuccess()) {  
             System.out.println("Client connected...");  
             channelFuture.channel().writeAndFlush("Hello World!!!\n");  
           } else {  
             System.out.println(channelFuture.cause());  
           }  
         }  
       });  
       // Wait until the connection is closed.  
       f.channel().closeFuture().sync();  
     } finally {  
       workerGroup.shutdownGracefully();  
     }  
   
   }  
 }  

The client code is trying to connect server on the specified port and once the connection is successful.it is sending "Hello World!!!\n" .  It is also sysout the response it receives from server.

Lets check out the outcome of the above code:

Client output:







Server output:






Lets make some changes to this code.
In the server code I am putting 5 second sleep before it respond to client.

 @Override  
     public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {  
       System.out.println(msg + " received by server");  
       Thread.sleep(5000l);  
       ctx.writeAndFlush("Hello from server\n");  
     }  


In the client code I am making below changes:
2 sysout before and after SENDING response to the server and calculate the delay in response.
Server respond back once it gets anything from client.
In Netty IO operations are asynchronous and we are handling server response in MyClientChannelHandler class. We print what ever is available from server when received.

  public void operationComplete(ChannelFuture channelFuture) throws Exception {  
           if (channelFuture.isSuccess()) {  
             System.out.println("Client connected...");  
             System.out.println("Waiting for server response...");  
             long startTime=System.nanoTime();  
             channelFuture.channel().writeAndFlush("Hello World!!!\n");  
             System.out.println("Server response received...Time taken:"+(System.nanoTime()-startTime)/1000000000.0+" seconds");  
           } else {  
             System.out.println(channelFuture.cause());  
           }  
         }  

Lets see the above change in action:
Client output:









You can see that channelFuture.channel().writeAndFlush("Hello World!!!\n"); writes to channel and returns immediately back.What ever returned by server will be handled in MyClientChannelHandler class when ever it will be available. The very important thing is the client thread will not be blocked and will not be waiting for server response in blocking manner. We are registering handlers with the channel. When ever any data available the client will handle it in non-blocking manner.

I will cover Netty programming in depth in future articles.

Please post your comments and doubts!!!