+ All Categories
Home > Technology > Netty Cookbook - Chapter 2

Netty Cookbook - Chapter 2

Date post: 12-Feb-2017
Category:
Upload: trieu-nguyen
View: 248 times
Download: 3 times
Share this document with a friend
27
2 Solving common network programming problems In chapter 1, you have learned core concepts and classes in Netty to build a networkingbased application. In this chapter, we will cover common problems and how to solve when building networkbased application. You will learn how to: Getting the local SocketAddress and the remote SocketAddress Sending and receiving data in Streambased 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 PubSub Netty server.
Transcript
Page 1: Netty Cookbook - Chapter 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. 

 

Page 2: Netty Cookbook - Chapter 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())); 

 

Page 3: Netty Cookbook - Chapter 2

 

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. 

 

Page 4: Netty Cookbook - Chapter 2

 

 

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 

 

Page 5: Netty Cookbook - Chapter 2

 

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 { 

 

Page 6: Netty Cookbook - Chapter 2

 

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; 

 

Page 7: Netty Cookbook - Chapter 2

 

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; 

 

Page 8: Netty Cookbook - Chapter 2

 

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()); 

 

Page 9: Netty Cookbook - Chapter 2

 

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); 

 

Page 10: Netty Cookbook - Chapter 2

 

}; 

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 

 

Page 11: Netty Cookbook - Chapter 2

 

ServerBootstrap publicServerBootstrap = new ServerBootstrap();   

publicServerBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.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();   

 

Page 12: Netty Cookbook - Chapter 2

 

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>(); 

 

Page 13: Netty Cookbook - Chapter 2

 

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 { 

 

Page 14: Netty Cookbook - Chapter 2

 

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 

 

Page 15: Netty Cookbook - Chapter 2

 

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(); 

 

Page 16: Netty Cookbook - Chapter 2

 

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 { 

 

Page 17: Netty Cookbook - Chapter 2

 

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(); 

 

 

Page 18: Netty Cookbook - Chapter 2

 

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) 

 

Page 19: Netty Cookbook - Chapter 2

 

             .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 

 

Page 20: Netty Cookbook - Chapter 2

 

             .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. 

 

Page 21: Netty Cookbook - Chapter 2

 

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()); 

 

Page 22: Netty Cookbook - Chapter 2

 

            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 

 

Page 23: Netty Cookbook - Chapter 2

 

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); 

 

Page 24: Netty Cookbook - Chapter 2

 

server.close(); 

In code of ClientAvroRPC 

public class ClientAvroRPC { 

    public static void main(String[] args) throws IOException {   

  if(args.length < 3){ 

  args = new String[] {"[email protected]","[email protected]","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": "[email protected]", "from": "[email protected]", "body": "Hello !"} 

 

Page 25: Netty Cookbook - Chapter 2

 

Result: Sending message to [email protected] from [email protected] 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(); 

 

Page 26: Netty Cookbook - Chapter 2

 

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(); 

 

Page 27: Netty Cookbook - Chapter 2

 

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); 

 

 


Recommended