Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Netty Cookbook - Chapter 2

526 views

Published on

Common network problems

Published in: Technology
  • Be the first to comment

Netty Cookbook - Chapter 2

  1. 1.   2  Solving common network  programming problems  In chapter 1, you have learned core concepts and classes in Netty to build a  networking­based application. In this chapter, we will cover common problems and  how to solve when building network­based application. You will learn how to:  ● Getting the local SocketAddress and the remote SocketAddress  ● Sending and receiving data in Stream­based TCP/IP Transport  ● Sending data in POJO way  ● Listening multiple sockets in one Netty instance  ● Building your own protocol servers and clients with Custom Codec  ● Counting Server Bandwidth in ChannelHandler  ● Checking Heartbeat Server using UDP protocol  ● Using Stream Control Transmission Protocol (SCTP) codec  ● Building simple FTP server  ● Building server RPC (Remote Procedure Call) with Apache Avro and Netty  ● Building simple HTTP file downloader  Introduction  In chapter 2, from recipe 2.1 to 2.6 will guide you how to use Netty in common use  cases,  with most important concept is Codec. From recipe 2.7 to 2.12,  we will cover  some examples with popular network protocol, such as TCP/IP, UDP , SCTP, FTP , RPC  with Apache Avro,  downloading file from HTTP and building a simple Pub­Sub Netty  server.   
  2. 2.   Getting ready  Make sure you check out this code at the Git repository of book  https://github.com/trieu/netty­cookbook  In this chapter, we would use the class ​netty.cookbook.common.BootstrapTemplate  to create common Netty’s bootstrap for both server code and client code.  Recipe 2.1 Getting the local SocketAddress and the  remote SocketAddress  Problem:  Your server needs to log the remote or local SocketAddress, this recipe could be useful  for debugging.   How to do it …  In any implemented class of ChannelInboundHandler or ChannelOutboundHandler,  your code must override the method “channelActive” and you can use the instance of  ChannelHandlerContext to get all information about the active connected channel.  This example code would illustrate how to do:  public​ ​void​ channelActive​(​ChannelHandlerContext​ ctx)  throws​ ​Exception​ {  InetSocketAddress​ localAddr ​=​ ​(​InetSocketAddress​)  ctx​.​channel​().​localAddress​();  logger​.​info​(​String​.​format​(​"localAddress.hostname  %s"​,​localAddr​.​getHostName​()));  logger​.​info​(​String​.​format​(​"localAddress.port %s"​,​localAddr​.​getPort​()));     InetSocketAddress​ remoteAddr ​=​ ​(​InetSocketAddress​)  ctx​.​channel​().​remoteAddress​();  logger​.​info​(​String​.​format​(​"remoteAddress.hostname  %s"​,​remoteAddr​.​getHostName​()));  logger​.​info​(​String​.​format​(​"remoteAddress.port  %s"​,​remoteAddr​.​getPort​()));  }   
  3. 3.   How it works  We can see the output from above code.  2014​­​11​­​27​ ​06​:​11​:​06​ INFO  ​LoggingHandler​:​100​ ​­​ ​[​id​:​ ​0x8db2146f​]​ REGISTERED  2014​­​11​­​27​ ​06​:​11​:​06​ INFO  ​LoggingHandler​:​100​ ​­​ ​[​id​:​ ​0x8db2146f​]  BIND​(​0.0​.​0.0​/​0.0​.​0.0​:​8007)  2014​­​11​­​27​ ​06​:​11​:​06​ INFO  ​LoggingHandler​:​100​ ​­​ ​[​id​:​ ​0x8db2146f​,  /​0​:​0​:​0​:​0​:​0​:​0​:​0​:​0​:​8007​]​ ACTIVE  2014​­​11​­​27​ ​06​:​11​:​12​ INFO  ​LoggingHandler​:​100​ ​­​ ​[​id​:​ ​0x8db2146f​,  /​0​:​0​:​0​:​0​:​0​:​0​:​0​:​0​:​8007​]​ RECEIVED​:​ ​[​id​:​ ​0x90524633​,​ ​/127.0.0.1:46697 =>  /​127.0​.​0.1​:​8007]  2014​­​11​­​27​ ​06​:​11​:​12​ INFO  ​PurchaseServer​:​47​ ​­​ localAddress​.​hostname localhost  2014​­​11​­​27​ ​06​:​11​:​12​ INFO  ​PurchaseServer​:​48​ ​­​ localAddress​.​port ​8007  2014​­​11​­​27​ ​06​:​11​:​12​ INFO  ​PurchaseServer​:​51​ ​­​ remoteAddress​.​hostname localhost  2014​­​11​­​27​ ​06​:​11​:​12​ INFO  ​PurchaseServer​:​52​ ​­​ remoteAddress​.​port ​46697  Recipe 2.2 Sending and receiving data in  Stream­based TCP/IP Transport  Problem:  When sending data over the TCP/IP, data usually must be divided into smaller packets.  That’s the mechanism of a stream­based transport such as TCP/IP. For example, if you  want to send a long messages, the protocol transport itself  would divide the message  as independent set of bytes. Also, there is no guarantee that what you could receive is  exactly what your remote peer sent.  Solution:  Netty provides an extensible class which helps you solve this problem more elegant.  One simple way you could do , by extending the class ByteToMessageDecoder or  ReplayingDecoder to encode and decode received data into one or more meaningful  frames instead of bytes. It’s also safer for sending and receiving a large data message  in stream­based transport such as TCP/IP.   
  4. 4.     How to do it …  We could split MessageClientHandler into two handlers:  ● MessageDecoder which deals with the fragmentation issue, and  ● the initial simple version of MessageClientHandler.  Netty provides extensible classes which simplify  code you would write, one of  common way to send/receive string message in Netty is using StringEncoder and  StringDecoder. Let’s see how simple code:  The sender class:  public​ ​class​ ​Sender​ {  static​ ​final​ ​int​ PORT ​=​ ​8007;  static​ ​final​ ​String​ HOST ​=​ ​"127.0.0.1";    public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​InterruptedException​ ​{    final​ ​String​ msg ​=​ ​"This is a long message";    ChannelInitializer​<​SocketChannel​>​ initializer ​=​ ​new  ChannelInitializer​<​SocketChannel​>()​ {  @Override   
  5. 5.   public​ ​void​ initChannel​(​SocketChannel​ ch​)​ ​throws​ ​Exception  {  ChannelPipeline​ p ​=​ ch​.​pipeline​();  p​.​addLast​(​new​ ​StringEncoder​());  p​.​addLast​(​new​ ​StringDecoder​());  p​.​addLast​(​new​ ​ChannelInboundHandlerAdapter​()​ {  @Override  public​ ​void  channelActive​(​ChannelHandlerContext​ ctx)  throws​ ​Exception​ {  //on ready to send  ctx​.​writeAndFlush​(​msg​);  }  @Override  public​ ​void  channelRead​(​ChannelHandlerContext​ ctx,  Object​ data​)​ ​throws​ ​Exception  {  //on receive  System​.​out​.​println​(​"got "​ ​+​ data​);  }  });  }  };  BootstrapTemplate​.​newClientBootstrap​(​HOST​,​ PORT​,​ initializer ​);  Thread​.​sleep​(​5000​);  }  }  The receiver class  public​ ​class​ ​Receiver​ {  static​ ​final​ ​int​ PORT ​=​ ​8007;  static​ ​final​ ​String​ HOST ​=​ ​"127.0.0.1";  public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​Exception​ {   
  6. 6.   ChannelInitializer​<​SocketChannel​>​ initializer ​=​ ​new  ChannelInitializer​<​SocketChannel​>()​ {  @Override  public​ ​void​ initChannel​(​SocketChannel​ ch​)​ ​throws​ ​Exception  {  ChannelPipeline​ p ​=​ ch​.​pipeline​();  p​.​addLast​(​new​ ​StringEncoder​());  p​.​addLast​(​new​ ​StringDecoder​());  p​.​addLast​(​new​ ​ChannelInboundHandlerAdapter​()​ {  @Override  public​ ​void  channelRead​(​ChannelHandlerContext​ ctx​,​ ​Object​ msg​)​ ​throws​ ​Exception​ {  System​.​out​.​println​(​msg​);  ctx​.​close​();  }  });  }  };  BootstrapTemplate​.​newServerBootstrap​(​HOST​,​ PORT​,​ initializer​);  }  }  Recipe 2.3 Sending data in POJO way  Problem  The POJO or  Plain Old Java Object, is common way to encoding business logic into  regular java objects rather than using Entity Beans. Netty supports encoding/decoding  object into ByteBuf and vice versa.  Example we have this business object for modelling the data of a purchase transaction  between user and e­commerce back­end server.     public​ ​class​ ​PurchaseData​ ​implements​ ​Serializable{  private​ ​final​ ​int​ itemId;   
  7. 7.   private​ ​final​ ​float​ price;  private​ ​final​ ​String​ buyer;  private​ ​final​ ​String​ seller;  private​ ​final​ ​int​ unixTime;  private​ ​final​ ​boolean​ processed;  How to do it …  Let’s check this diagram    Netty supports 2 classes, ObjectDecoder and ObjectEncoder, that simplifies how we  decoding/encoding POJO objects  In the method initChannel, add this code for POJO codec:          ​ChannelPipeline​ p ​=​ ch​.​pipeline​();   ​ClassResolver​ cl ​=​ ​ClassResolvers​.​weakCachingConcurrentResolver​(​null​);   p​.​addLast​(​"decoder"​,​ ​new​ ​ObjectDecoder​(​cl​));      p​.​addLast​(​"encoder"​,​ ​new​ ​ObjectEncoder​());   Here is the full code of PurchaseData class  public​ ​class​ ​PurchaseData​ ​implements​ ​Serializable{  private​ ​static​ ​final​ ​long​ serialVersionUID ​=​ ​­​5467453661148034694L;  private​ ​final​ ​int​ itemId;   
  8. 8.   private​ ​final​ ​float​ price;  private​ ​final​ ​String​ buyer;  private​ ​final​ ​String​ seller;  private​ ​final​ ​int​ unixTime;  private​ ​final​ ​boolean​ processed;  public​ ​PurchaseData​(​int​ itemId​,​ ​float​ price​,​ ​String​ buyer​,​ ​String  seller, ​int​ unixTime​,​ ​boolean​ processed​)​ {  super​();  this​.​itemId ​=​ itemId;  this​.​price ​=​ price;  this​.​buyer ​=​ buyer;  this​.​seller ​=​ seller;  this​.​unixTime ​=​ unixTime;  this​.​processed ​=​ processed;  }   public​ ​PurchaseData​(​Object​ obj​,​ ​boolean​ processed​)​ {  super​();  PurchaseData​ data ​=​ ​(​PurchaseData​)​obj;  this​.​itemId ​=​ data​.​itemId;  this​.​price ​=​ data​.​price;  this​.​buyer ​=​ data​.​buyer;  this​.​seller ​=​ data​.​seller;  this​.​unixTime ​=​ data​.​unixTime;  this​.​processed ​=​ processed;  }  The code of PurchaseServer  ChannelInitializer​<​SocketChannel​>​ initializer ​=​ ​new  ChannelInitializer​<​SocketChannel​>()​ {  @Override  public​ ​void​ initChannel​(​SocketChannel​ ch​)​ ​throws​ ​Exception​ {  ChannelPipeline​ p ​=​ ch​.​pipeline​();  p​.​addLast​(​new​ ​PurchaseDataDecoder​());  p​.​addLast​(​new​ ​PurchaseDataEncoder​());   
  9. 9.   p​.​addLast​(​new​ ​ChannelInboundHandlerAdapter​()​ {  @Override  public​ ​void​ channelRead​(​ChannelHandlerContext​ ctx,  Object​ data​)​ ​throws​ ​Exception​ {  System​.​out​.​println​(​"processed Purchase "​ ​+​ data​);  PurchaseData​ processed ​=​ ​new​ ​PurchaseData​(​data​,  true​);  ctx​.​writeAndFlush​(​processed​);  }  });  }  };  BootstrapTemplate​.​newServerBootstrap​(​HOST​,​ PORT​,​ initializer​);  The code of PurchaseClient  public​ ​class​ ​PurchaseClient​ {  String​ host​;​ ​int​ port;      ​public​ ​PurchaseClient​(​String​ host​,​ ​int​ port​)​ {  super​();  this​.​host ​=​ host;  this​.​port ​=​ port;  }        ​public​ ​PurchaseClient​ send​(​PurchaseData​ message​,​ ​CallbackProcessor  asynchCall​)​ ​throws​ ​Exception{    ChannelHandler​ clientHandler ​=​ ​new​ ​PurchaseClientHandler​(​message​,  asynchCall​);    ChannelInitializer​<​SocketChannel​>​ initializer ​=​ ​new  ChannelInitializer​<​SocketChannel​>()​ {  @Override  public​ ​void​ initChannel​(​SocketChannel​ ch​)​ ​throws​ ​Exception  {  ChannelPipeline​ p ​=​ ch​.​pipeline​();  p​.​addLast​(​new​ ​PurchaseDataDecoder​());  p​.​addLast​(​new​ ​PurchaseDataEncoder​());  p​.​addLast​(​clientHandler​);   
  10. 10.   }  };  BootstrapTemplate​.​newTcpClientBootstrap​(​host​,​ port​,​ initializer  );    return​ ​this;      ​}        ​public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​Exception​ ​{      int​ time ​=​ ​(​int​)​ ​(​System​.​currentTimeMillis​()​ ​/​ ​1000L​);  PurchaseData​ data ​=​ ​new​ ​PurchaseData​(​1001​,​ ​499.99f​,​ ​"Trieu"​,  "Amazon"​,​ time​,​ ​false​ ​);    new​ ​PurchaseClient​(​"127.0.0.1"​,​8007​).​send​(​data​,​ rs ​­>​ {    System​.​out​.​println​(​rs​);      });        }  }  Recipe 2.4 Listening multiple sockets in one Netty  instance  Problem:  In networking development, sometimes you want your server listen multiple sockets  in one instance. Example , you could implement a HTTP server , which would listen in  2 sockets. The first one for public, that serves all traffic requests from Internet.  The  second one for private usage, which only internal networking management or  networking monitoring.  How to do it   We  use 2 independent objects of ServerBootstrap to solve this problem. Each of them  bind to different socket, so the request would be processed in 2 independent  instances of ChannelHandler. Loot at this code:    try​ ​{    //public service processor   
  11. 11.   ServerBootstrap​ publicServerBootstrap ​=​ ​new​ ​ServerBootstrap​();    publicServerBootstrap​.​group​(​bossGroup​,​workerGroup​).​channel​(​NioServerSocketChanne l​.​class​);    //bind to public access host info  Channel​ ch1;  if​(​"*"​.​equals​(​ip​)){  ch1 ​=​ publicServerBootstrap​.​bind​(​port​).​sync​().​channel​();  }​ ​else​ {  ch1 ​=​ publicServerBootstrap​.​bind​(​ip​,​ port​).​sync​().​channel​();  }  ch1​.​config​().​setConnectTimeoutMillis​(​1800​);  //admin service processor  ServerBootstrap​ adminServerBootstrap ​=​ ​new​ ​ServerBootstrap​();    adminServerBootstrap​.​group​(​bossGroup​,​workerGroup​).​channel​(​NioServerSocketChannel .​class)  .​childOption​(​ChannelOption​.​TCP_NODELAY​,​ ​false​)  .​childOption​(​ChannelOption​.​SO_KEEPALIVE​,​ ​false)    .​childHandler​(​new  PrivateHttpServerInitializer​(​DEFAULT_CLASSPATH​,​this​.​privatePoolSize ​));    //bind to private access (for administrator only) host info, default 10000  Channel​ ch2;  if​(​"*"​.​equals​(​ip​)){  ch2 ​=​ adminServerBootstrap​.​bind​(​privatePort​).​sync​().​channel​();  }​ ​else​ {  ch2 ​=​ adminServerBootstrap​.​bind​(​ip​,​privatePort​).​sync​().​channel​();  }    ch2​.​config​().​setConnectTimeoutMillis​(​1800​);    LogUtil​.​i​(​"publicServerBootstrap "​,​ ​"is started and listening at "​ ​+​ ​this​.​ip ​+  ":"​ ​+​ ​this​.​port​);  LogUtil​.​i​(​"adminServerBootstrap "​,​ ​"is started and listening at "​ ​+​ ​this​.​ip ​+  ":"​ ​+​ privatePort​);  ch1​.​closeFuture​().​sync​();     
  12. 12.   ch2​.​closeFuture​().​sync​();  System​.​out​.​println​(​"Shutdown..."​);  Recipe 2.5 Counting Server Bandwidth Meter  Problem  One common problem in network monitoring is how we can measure the use of  bandwidth in your Netty application. In this recipe , we will cover how to do in by  adding modification in pipeline and it will measure the size of sent/received  ByteBuffer.  How to do it    Look at the diagram, we would hook the code into decode codec and encode codec.  This code illustrates the solution  public​ ​class​ ​NettyMonitorIO​ {  static​ ​final​ ​DateFormat​ DATE_TIME_FORMAT ​=​ ​new  SimpleDateFormat​(​"yyyy­MM­dd HH:mm"​);  static​ ​ConcurrentMap​<​String​,​ ​Long​>​ dataOutStats ​=​ ​new  ConcurrentHashMap​<​String​,​ ​Long​>();  static​ ​ConcurrentMap​<​String​,​ ​Long​>​ dataInStats ​=​ ​new  ConcurrentHashMap​<​String​,​ ​Long​>();   
  13. 13.   public​ ​static​ ​long​ updateDataOut​(​ByteBuf​ buf​)​ {  String​ time ​=​ DATE_TIME_FORMAT​.​format​(​new​ ​Date​());  long​ c ​=​ dataOutStats​.​getOrDefault​(​time​,​ ​0L​)​ ​+  buf​.​readableBytes​();  dataOutStats​.​put​(​time​,​ c​);  return​ c;  }   public​ ​static​ ​long​ updateDataIn​(​ByteBuf​ buf​)​ {  String​ time ​=​ DATE_TIME_FORMAT​.​format​(​new​ ​Date​());  long​ c ​=​ dataInStats​.​getOrDefault​(​time​,​ ​0L​)​ ​+  buf​.​writableBytes​();  dataInStats​.​put​(​time​,​ c​);  return​ c;  }  static​ {  new​ ​Timer​(​true​).​schedule​(​new​ ​TimerTask​()​ {  @Override  public​ ​void​ run​()​ {  System​.​out​.​println​(​"­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­"​);  System​.​out​.​println​(​"Data In Stats:"​);  dataInStats​.​forEach​((​String​ key​,​ ​Long​ val​)­>{  LogUtil​.​println​(​key ​+​ ​" : "​+​val​);  });  System​.​out​.​println​(​"Data Out Stats:"​);  dataOutStats​.​forEach​((​String​ key​,​ ​Long​ val​)­>{  LogUtil​.​println​(​key ​+​ ​" : "​+​val​);  });  }  },​ ​2000​,​ ​2000​);  }  }  public​ ​class​ ​PurchaseDataDecoder​ ​extends​ ​ObjectDecoder​ {   
  14. 14.   public​ ​PurchaseDataDecoder​()​ {  super​(​ClassResolvers​.​weakCachingConcurrentResolver​(​null​));   }  @Override  protected​ ​Object​ decode​(​ChannelHandlerContext​ ctx​,​ ​ByteBuf​ buf)  throws​ ​Exception​ {  Object​ ​object​ ​=​ ​super​.​decode​(​ctx​,​ buf​);  NettyMonitorIO​.​updateDataIn​(​buf​);  return​ ​object;  }  }  public​ ​class​ ​PurchaseDataEncoder​ ​extends​ ​ObjectEncoder​ {  @Override  protected​ ​void​ encode​(​ChannelHandlerContext​ ctx​,​ ​Serializable​ msg​,  ByteBuf​ buf​)​ ​throws​ ​Exception​ ​{   super​.​encode​(​ctx​,​ msg​,​ buf​);  NettyMonitorIO​.​updateDataOut​(​buf​);  }  }    How it works  2014​­​12​­​05​ ​10​:​56​:​35​ INFO  ​LoggingHandler​:​101​ ​­​ ​[​id​:​ ​0x1fbe5190​]​ REGISTERED  2014​­​12​­​05​ ​10​:​56​:​35​ INFO  ​LoggingHandler​:​101​ ​­​ ​[​id​:​ ​0x1fbe5190​]  BIND​(​0.0​.​0.0​/​0.0​.​0.0​:​8007)  2014​­​12​­​05​ ​10​:​56​:​35​ INFO  ​LoggingHandler​:​101​ ​­​ ​[​id​:​ ​0x1fbe5190​,​ ​/​0​:​0​:​0​:​0​:​0​:​0​:​0​:​0​:​8007​]  ACTIVE  2014​­​12​­​05​ ​10​:​56​:​39​ INFO  ​LoggingHandler​:​101​ ​­​ ​[​id​:​ ​0x1fbe5190​,​ ​/​0​:​0​:​0​:​0​:​0​:​0​:​0​:​0​:​8007​]  RECEIVED​:​ ​[​id​:​ ​0x10fdf42c​,​ ​/127.0.0.1:33991 => /​127.0​.​0.1​:​8007]  2014​­​12​­​05​ ​10​:​56​:​39​ INFO  ​PurchaseServer​:​33​ ​­​ localAddress​.​hostname localhost  2014​­​12​­​05​ ​10​:​56​:​39​ INFO  ​PurchaseServer​:​34​ ​­​ localAddress​.​port ​8007  2014​­​12​­​05​ ​10​:​56​:​39​ INFO  ​PurchaseServer​:​37​ ​­​ remoteAddress​.​hostname localhost  2014​­​12​­​05​ ​10​:​56​:​39​ INFO  ​PurchaseServer​:​38​ ​­​ remoteAddress​.​port ​33991   
  15. 15.   processed ​Purchase  {​"itemId"​:​1001​,​"price"​:​499.99​,​"buyer"​:​"Trieu"​,​"seller"​:​"Amazon"​,​"unixTime"​:​1417751798​,​" processed"​:​false}  ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  Data​ ​In​ ​Stats:  2014​­​12​­​05​ ​10​:​56​ ​:​ ​942  Data​ ​Out​ ​Stats:  2014​­​12​­​05​ ​10​:​56​ ​:​ ​82    Recipe 2.6 Checking Heartbeat Server using UDP  Problem  Sometimes we don’t  want to use TCP/IP for transmitting data, because the reliability  is not our priority. By  UDP is a connection­less protocol, so it’s more better than  TCP/IP  in many applications, such as:  ● Broadcasting message in a publish­subscribe kind of application.  ● One­way "logging" activity can be handled nicely with UDP packets  ● Implementing "heartbeat" or "I'm alive" messages, because we want to get a  simple answer to another server quickly  ● Video streaming and especially VoIP, voice chat ,..(e.g. Skype), that a dropped  packet is not such a big deal.  How to do it …  At server, loading Bootrap object with “option(ChannelOption.SO_BROADCAST, true)”  The handler of server should extend SimpleChannelInboundHandler.   The server UDP socket is bind at 255.255.255.255, port 8080  Suppose we have an ImportantServer , that must be monitored.  public​ ​class​ ​ImportantServer​ {  private​ ​static​ ​final​ ​int​ PORT ​=​ ​8080;  public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​Exception​ {  EventLoopGroup​ loopGroup ​=​ ​new​ ​NioEventLoopGroup​();   
  16. 16.   try​ ​{   BootstrapTemplate​.​newBootstrapUDP​(​loopGroup​,​ ​new  HeartBeatHandler​(),​ PORT)  .​sync​().​channel​().​closeFuture​().​await​();  }​ ​finally​ {  loopGroup​.​shutdownGracefully​();  }  }  }    The code method “channelRead0” of HeartBeatHandler   protected​ ​void​ channelRead0​(​ChannelHandlerContext​ ctx​,​ ​DatagramPacket​ packet​)  throws​ ​Exception​ {  System​.​err​.​println​(​packet​);  String​ s ​=​ packet​.​content​().​toString​(​CharsetUtil​.​UTF_8​);  System​.​out​.​println​(​s​);  ByteBuf​ buf ​=​ ​Unpooled​.​copiedBuffer​(​"I'm alive at "​+​new​ ​Date​(),  CharsetUtil​.​UTF_8​);  ctx​.​write​(​new​ ​DatagramPacket​(​buf​,​ packet​.​sender​()));  }  The ServerHealthChecker, which uses UDP as main protocol   SimpleChannelInboundHandler​<​DatagramPacket​>​ handler ​=​ ​new  SimpleChannelInboundHandler​<​DatagramPacket​>()​ {  @Override  public​ ​void​ exceptionCaught​(​ChannelHandlerContext​ ctx​,  Throwable​ cause​)​ {  cause​.​printStackTrace​();  ctx​.​close​();  }  @Override  protected​ ​void​ channelRead0​(​ChannelHandlerContext​ ctx​,  DatagramPacket​ msg)  throws​ ​Exception​ {   
  17. 17.   String​ response ​=  msg​.​content​().​toString​(​CharsetUtil​.​UTF_8​);  System​.​out​.​println​(​"LogClient: "​ ​+​ response​);  ctx​.​close​();  }  };  EventLoopGroup​ loopGroup ​=​ ​new​ ​NioEventLoopGroup​();  try​ {  ChannelFuture​ chnlFuture ​=  BootstrapTemplate​.​newBootstrapUDP​(​loopGroup​,​ handler​,​ ​0​);  Channel​ ch ​=​ chnlFuture​.​sync​().​channel​();  // Broadcast to port 8080  InetSocketAddress​ udpAddr ​=​  ​new  InetSocketAddress​(​"255.255.255.255"​,​ PORT​);  for​ ​(​int​ i​=​0​;​i​<​5​;​i​++)​ {  ByteBuf​ buf ​=​ ​Unpooled​.​copiedBuffer​(​"ping at "​ ​+  new​ ​Date​(),​CharsetUtil​.​UTF_8​);  ch​.​write​(​new​ ​DatagramPacket​(​buf​,​udpAddr​));  Thread​.​sleep​(​1000​);  }  ch​.​flush​().​closeFuture​().​sync​();  //If the channel is not closed within 5 seconds, quit   if​ ​(!​ch​.​closeFuture​().​await​(​10000​))​ {  System​.​err​.​println​(​"request timed out."​);  }  }​ ​finally​ {  loopGroup​.​shutdownGracefully​();  }     
  18. 18.   Recipe 2.9 Using Stream Control Transmission  Protocol (SCTP) codec  Getting to know  SCTP (Stream Control Transmission Protocol) is a protocol for transmitting multiple  streams of data at the same time between two end points that have established a  connection in a network. Sometimes referred to as "next generation TCP"  (Transmission Control Protocol).  SCTP is a natural candidate to support telephone signaling over the Internet as well as  other message­oriented applications. Applications that require high degrees of fault  tolerance, such as online banking and stock market transaction exchanges, will benefit  from SCTP's multihoming.  You can follow this link for more information about SCTP protocol  http://www.ibm.com/developerworks/library/l­sctp  Problem  Your want to build a SCTP server that accepts and prints out all received messages,  and SCTP client that send multipe messages at the same time.  Getting ready  In linux OS you need to install lksctp library as linux doesn't have this installed by  default.  ● Fedora: sudo yum install lksctp­tools­1.0.9­1.fc10 or  ● Ubuntu: sudo apt­get install lksctp­tools  How to do it ...  In SCTP server bootstrap  EventLoopGroup​ mainLoop ​=​ ​new​ ​NioEventLoopGroup​(​1​);  EventLoopGroup​ workerLoop ​=​ ​new​ ​NioEventLoopGroup​();  try​ {         ​ChannelFuture​ f ​=​ ​new​ ​ServerBootstrap​().​group​(​mainLoop​,​ workerLoop)               ​.​channel​(​NioSctpServerChannel​.​class)   
  19. 19.                ​.​option​(​ChannelOption​.​SO_BACKLOG​,​ ​100)               ​.​handler​(​new​ ​LoggingHandler​(​LogLevel​.​INFO​))               ​.​childHandler​(​new​ ​ChannelInitializer​<​SctpChannel​>()​ {                   ​@Override                   ​public​ ​void​ initChannel​(​SctpChannel​ ch​)​ ​throws​ ​Exception​ {     ​ChannelPipeline​ p ​=​ ch​.​pipeline​();                         p​.​addLast​(​new​ ​SimpleSctpServerHandler​());                   }               ​}).​bind​(​PORT​).​sync​();                f​.​channel​().​closeFuture​().​sync​();          ​}​ ​finally​ ​{                mainLoop​.​shutdownGracefully​();              workerLoop​.​shutdownGracefully​();          }  }  In the code of SCTP Server handler, at channelRead, the message is instance of  SctpMessage  public​ ​void​ channelRead​(​ChannelHandlerContext​ ctx​,​ ​Object​ msg​)​ {    System​.​out​.​println​(​msg​);    if​(​msg ​instanceof​ ​SctpMessage​){     SctpMessage​ sctpMsg ​=​ ​(​SctpMessage​)​ msg;    System​.​out​.​println​(​sctpMsg​.​content​().​toString​(​CharsetUtil​.​UTF_8​));    ctx​.​write​(​sctpMsg​);     }        }  The SCTP client bootstrap   ​EventLoopGroup​ loopGroup ​=​ ​new​ ​NioEventLoopGroup​();          ​try​ ​{      ChannelFuture​ f ​=​ ​new​ ​Bootstrap​().​group​(​loopGroup)               ​.​channel​(​NioSctpChannel​.​class)               ​// set SCTP option   
  20. 20.                ​.​option​(​SctpChannelOption​.​SCTP_NODELAY​,​ ​true​)                ​.​handler​(​new​ ​ChannelInitializer​<​SctpChannel​>()​ {                   ​@Override                   ​public​ ​void​ initChannel​(​SctpChannel​ ch​)​ ​throws​ ​Exception​ {     ​ChannelPipeline​ p ​=​ ch​.​pipeline​();                       p​.​addLast​(​new​ ​SimpleSctpClientHandler​());                   }               ​}).​connect​(​HOST​,​ PORT​).​sync​();                f​.​channel​().​closeFuture​().​sync​();          ​}​ ​finally​ {    loopGroup​.​shutdownGracefully​();          }  The SCTP client handler code   ​private​ ​final​ ​ByteBuf​ firstMessage​,​ secondMessage;      ​public​ ​SimpleSctpClientHandler​()​ {          firstMessage ​=​ ​Unpooled​.​copiedBuffer​(​"first message"​,​CharsetUtil​.​UTF_8​);          secondMessage ​=​ ​Unpooled​.​copiedBuffer​(​"second  message"​,​CharsetUtil​.​UTF_8​);      }      ​@Override      ​public​ ​void​ channelActive​(​ChannelHandlerContext​ ctx​)​ {          ctx​.​write​(​new​ ​SctpMessage​(​0​,​ ​0​,​ firstMessage​));          ctx​.​write​(​new​ ​SctpMessage​(​0​,​ ​0​,​ secondMessage​));          ctx​.​flush​();      }    Recipe 2.10 Building simple FTP server  Problem  FTP  or File Transfer Protocol is most common protocol for transferring files between  client and server. We will learn to use Netty to build a simple FTP server in this recipe.   
  21. 21.   Getting ready  Due to the core Netty 4 is still support completely FTP Channel Handler, so for rapid  development, you should check out this source code   https://github.com/codingtony/netty­ftp­receiver  It’s the Netty handler, partial implementation of RFC 959 "File Transfer Protocol (FTP)"  for receiving FTP files.   How to do it …  You define the template for receiving data with FTP protocol.  Example code of the data receiver class :  class​ ​FileReceiver​ ​implements​ ​DataReceiver​ {      ​@Override      ​public​ ​void​ receive​(​String​ name​,​ ​InputStream​ data​)​ ​throws​ ​IOException​ {          ​System​.​out​.​println​(​"got file: ["​ ​+​ name ​+​ ​"]"​);          ​//copy to local folder          ​Files​.​copy​(​data​,​ ​new​ ​File​(​"./data/"​+​name​).​toPath​());        }  }  In Netty’s bootstrap code, add 2 classes: CrlfStringDecoder and FtpServerHandler to  ChannelPipeline  public​ ​class​ ​SimpleServerFTP​ {  public​ ​static​ ​void​ main​(​String​...​ args​)​ ​throws​ ​Exception​ {  DataReceiver​ dataReceiver ​=​ ​new​ ​FileReceiver​();    final​ ​DefaultCommandExecutionTemplate​ tpl ​=​ ​new  DefaultCommandExecutionTemplate​(​dataReceiver​);      ChannelInitializer​<​SocketChannel​>​ initializer ​=​ ​new  ChannelInitializer​<​SocketChannel​>()​ {  @Override  protected​ ​void​ initChannel​(​SocketChannel​ ch​)​ ​throws  Exception​ {  ChannelPipeline​ p ​=​ ch​.​pipeline​();              p​.​addLast​(​new​ ​CrlfStringDecoder​());   
  22. 22.               p​.​addLast​(​new​ ​FtpServerHandler​(​tpl​));  }  };    BootstrapTemplate​.​newServerBootstrap​(​"127.0.0.1"​,​ ​2121​,​ initializer​);  }  }    Recipe 2.10 Building server RPC (Remote  Procedure Call) with Apache Avro  Problem  How could we make distributed programming look like as much as possible like  normal programming ?   One of simple solution is the RPC or the Remote Procedure Call, is a well­understood  mechanism and popular in distributed programming. Currently, Netty framework is  not fully implemented RPC, but there are many good and open source projects do this  job very well.  In this recipe, we will use Apache Avro, a data serialization system, which supports  RPC with Netty as core library.  To illustrate RPC, we would build a simple server to receive mail and a client to send  messages.  Getting to know  Avro is a remote procedure call, fully supports data serialization framework and  developed within Apache's Hadoop project. It uses JSON for defining data types and  protocols, and serializes data in a compact binary format. Main features:  ● Rich and complex data structures  ● A compact, fast and binary data format for streaming data between hosts  ● A container file, to store persistent data.  ● Remote procedure call (RPC) for distributed computing  ● Simple integration with dynamic languages, such as Python, Ruby, … so  integration between independent services   
  23. 23.   Getting ready  Make sure you check out this code at the Git repository of book  https://github.com/trieu/netty­cookbook  The full code at the package netty.cookbook.chapter2.recipe10    How to do it …  Optional step, first on all, we need to create Mail protocol , or the Interface Definition  Language to specify RPC call and return types.   In the current directory of netty­cookbook’s code, go to tools and run  java ​­​jar avro​­​tools​­​1.7​.​7.jar​ compile protocol ​../​src​/​main​/​avro​/​mail​.​avpr  ../​src​/​main​/​java/  In the ProxyServerAvroRPC   public​ ​class​ ​ProxyServerAvroRPC​ {  public​ ​static​ ​class​ ​MailImpl​ ​implements​ ​Mail​ {  public​ ​Utf8​ send​(​Message​ message​)​ ​{   String​ s ​=​ ​String​.​format​(​"message details, to:%s from:%s  body:%s"​,​ message​.​getTo​(),​  message​.​getFrom​(),​ message​.​getBody​());  LogUtil​.​println​(​s​);  return​ ​new​ ​Utf8​(​"Sent OK to "​+​ message​.​getTo​());  }  }  private​ ​static​ ​Server​ server;  static​ ​void​ startServer​()​ ​throws​ ​IOException​ {  server ​=​ ​new​ ​NettyServer​(​new​ ​SpecificResponder​(​Mail​.​class​,​new  MailImpl​()),​ ​new​ ​InetSocketAddress​(​10000​));  }  public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​Exception​ {  LogUtil​.​println​(​"Starting ServerAvroRPC"​);  startServer​();  LogUtil​.​println​(​"ServerAvroRPC started and wait for 15s"​);   Thread​.​sleep​(​15000​);   
  24. 24.   server​.​close​();  }  }  In code of ClientAvroRPC  public​ ​class​ ​ClientAvroRPC​ {      ​public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​IOException​ ​{      if​(​args​.​length ​<​ ​3​){    args ​=​ ​new​ ​String​[]  {​"someone@example.com"​,​"myself@example.com"​,​"Hello !"​};    }          ​NettyTransceiver​ client ​=​ ​new​ ​NettyTransceiver​(​new  InetSocketAddress​(​10000​));            ​Mail​ proxy ​=​ ​(​Mail​)​ ​SpecificRequestor​.​getClient​(​Mail​.​class​,​ client​);          ​LogUtil​.​println​(​"ClientAvroRPC built OK, got proxy, ready to send data  ..."​);            ​Message​ message ​=​ ​new​ ​Message​();          message​.​setTo​(​new​ ​Utf8​(​args​[​0​]));          message​.​setFrom​(​new​ ​Utf8​(​args​[​1​]));          message​.​setBody​(​new​ ​Utf8​(​args​[​2​]));          ​LogUtil​.​println​(​"Calling proxy.send with message:  "​ ​+  message​.​toString​());          ​LogUtil​.​println​(​"Result from server: "​ ​+​ proxy​.​send​(​message​));           client​.​close​();        }  }  How it works   When you start ProxyServerAvroRPC, the socket at port 10000 is listened.  The client, ClientAvroRPC would send a mail message “Hello !”, which is serialized and  is sent to ServerAvroRPC via the method send of the instance of Mail class. The output  when you run the code of ClientAvroRPC is:  Client​ built​,​ got proxy  Calling​ proxy​.​send ​with​ message​:​  ​{​"to"​:​ ​"someone@example.com"​,​ ​"from"​:  "myself@example.com"​,​ ​"body"​:​ ​"Hello !"}   
  25. 25.   Result​:​ ​Sending​ message to someone@example​.​com ​from​ myself@example​.​com ​with​ body  Hello​ !  Recipe 2.11 Building simple HTTP downloader  Problem:  Using Netty framework to build a HTTP client, that downloads one specific large file  from one specific host using one specific URL (https://example.com/a­big­file.zip) over  HTTP and saves it on disk.  Getting ready  Make sure you check out this code at the Git repository of book  https://github.com/trieu/netty­cookbook  The full code at the  package netty.cookbook.chapter2.recipe11, class  HttpDownloader.  How to do it …  We will use the method newHttpClientBootstrap of BootstrapTemplate to create new  bootstrap for a HTTP client, we set the URL with HttpDownloadHandler.  public​ ​class​ ​HttpDownloader​ {  public​ ​static​ ​class​ ​HttpDownloadHandler​ ​extends  SimpleChannelInboundHandler​<​HttpObject​>​ {  int​ written ​=​ ​0;  File​ file;  FileOutputStream​ outStream;  FileChannel​ fileChnl;  public​ ​HttpDownloadHandler​(​File​ file​)​ {  super​();  this​.​file ​=​ file;  }  void​ initFileChannel​()​ ​throws​ ​FileNotFoundException​ {  outStream ​=​ ​new​ ​FileOutputStream​(​file​);  fileChnl ​=​ outStream​.​getChannel​();   
  26. 26.   }  void​ writeBytesToFile​(​ByteBuf​ byteBuf​)​ ​throws​ ​IOException​ {  int​ writtenIndex ​=​ ​0;  try​ {  ByteBuffer​ byteBuffer ​=​ byteBuf​.​nioBuffer​();  writtenIndex ​+=​ fileChnl​.​write​(​byteBuffer​,  written​);  written ​+=​ writtenIndex;  byteBuf​.​readerIndex​(​byteBuf​.​readerIndex​()​ ​+  writtenIndex​);  fileChnl​.​force​(​false​);  }​ ​catch​ ​(​Exception​ e​)​ {  fileChnl​.​close​();  outStream​.​close​();  }  }  @Override  protected​ ​void​ channelRead0​(​ChannelHandlerContext​ ctx​,​ ​HttpObject  msg​)​ {  try​ {  if​ ​(​msg ​instanceof​ ​HttpRequest​)​ {  initFileChannel​();  }​ ​else​ ​if​ ​(​msg ​instanceof​ ​HttpContent​)​ {  if​ ​(​fileChnl ​==​ ​null​)​ {  initFileChannel​();  }  ByteBuf​ byteBuf ​=​ ​((​HttpContent​)  msg​).​content​();  writeBytesToFile​(​byteBuf​);  }​ ​else​ ​if​ ​(​msg ​instanceof​ ​LastHttpContent​)​ {  if​ ​(​fileChnl ​!=​ ​null​ ​&&​ outStream ​!=​ ​null​)  {  fileChnl​.​close​();  outStream​.​close​();   
  27. 27.   }  ctx​.​close​();  }  }​ ​catch​ ​(​IOException​ e​)​ {  e​.​printStackTrace​();  }  }  }  public​ ​static​ ​void​ main​(​String​[]​ args​)​ ​throws​ ​Exception​ {  String​ url ​=​ ​"http://www.mc2ads.com";  File​ file ​=​ ​new​ ​File​(​"./data/www.mc2ads.com.html"​);  ChannelHandler​ handler ​=​ ​new​ ​HttpDownloadHandler​(​file​);  BootstrapTemplate​.​newHttpClientBootstrap​(​url​,​ handler​);  }  }     

×