本文是学习Netty的第一篇文章,主要对Netty的Server和Client间的通讯机制进行验证。

Server与Client建立连接后,会执行以下的步骤:

1、Client向Server发送消息:Are you ok?

2、Server接收客户端发送的消息,并打印出来。

3、Server端向客户端发送消息:I am ok!

4、Client接收Server端发送的消息,并打印出来,通讯结束。

 

涉及到的类有4个:

1、HelloServer :server类,启动Netty server

2、HelloServerInHandler:server的handler,接收客户端消息,并向客户端发送消息

3、HelloClient:client类,建立于Netty server的连接

4、HelloClientIntHandler:client的handler,接收server端的消息,并向服务端发送消息

 

1、HelloServer代码如下:

[java] view plain copy
  1. package com.guowl.testserver;  
  2.   
  3. import io.netty.bootstrap.ServerBootstrap;  
  4. import io.netty.channel.ChannelFuture;  
  5. import io.netty.channel.ChannelInitializer;  
  6. import io.netty.channel.ChannelOption;  
  7. import io.netty.channel.EventLoopGroup;  
  8. import io.netty.channel.nio.NioEventLoopGroup;  
  9. import io.netty.channel.socket.SocketChannel;  
  10. import io.netty.channel.socket.nio.NioServerSocketChannel;  
  11.   
  12. public class HelloServer {  
  13.     public void start(int port) throws Exception {  
  14.         EventLoopGroup bossGroup = new NioEventLoopGroup();  
  15.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  16.         try {  
  17.             ServerBootstrap b = new ServerBootstrap();  
  18.             b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)  
  19.                     .childHandler(new ChannelInitializer<SocketChannel>() {  
  20.                         @Override  
  21.                         public void initChannel(SocketChannel ch)  
  22.                                 throws Exception {  
  23.                             // 注册handler  
  24.                             ch.pipeline().addLast(new HelloServerInHandler());  
  25.                         }  
  26.                     }).option(ChannelOption.SO_BACKLOG, 128)  
  27.                     .childOption(ChannelOption.SO_KEEPALIVE, true);  
  28.   
  29.             ChannelFuture f = b.bind(port).sync();  
  30.   
  31.             f.channel().closeFuture().sync();  
  32.         } finally {  
  33.             workerGroup.shutdownGracefully();  
  34.             bossGroup.shutdownGracefully();  
  35.         }  
  36.     }  
  37.   
  38.     public static void main(String[] args) throws Exception {  
  39.         HelloServer server = new HelloServer();  
  40.         server.start(8000);  
  41.     }  
  42. }  


2、HelloServerInHandler代码如下:

[java] view plain copy
  1. package com.guowl.testserver;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelInboundHandlerAdapter;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. // 该handler是InboundHandler类型  
  11. public class HelloServerInHandler extends ChannelInboundHandlerAdapter {  
  12.     private static Logger logger = LoggerFactory  
  13.             .getLogger(HelloServerInHandler.class);  
  14.   
  15.     @Override  
  16.     public void channelRead(ChannelHandlerContext ctx, Object msg)  
  17.             throws Exception {  
  18.         logger.info("HelloServerInHandler.channelRead");  
  19.         ByteBuf result = (ByteBuf) msg;  
  20.         byte[] result1 = new byte[result.readableBytes()];  
  21.         // msg中存储的是ByteBuf类型的数据,把数据读取到byte[]中  
  22.         result.readBytes(result1);  
  23.         String resultStr = new String(result1);  
  24.         // 接收并打印客户端的信息  
  25.         System.out.println("Client said:" + resultStr);  
  26.         // 释放资源,这行很关键  
  27.         result.release();  
  28.   
  29.         // 向客户端发送消息  
  30.         String response = "I am ok!";  
  31.         // 在当前场景下,发送的数据必须转换成ByteBuf数组  
  32.         ByteBuf encoded = ctx.alloc().buffer(4 * response.length());  
  33.         encoded.writeBytes(response.getBytes());  
  34.         ctx.write(encoded);  
  35.         ctx.flush();  
  36.     }  
  37.   
  38.     @Override  
  39.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
  40.         ctx.flush();  
  41.     }  
  42. }  


3、HelloClient代码如下:

[java] view plain copy
  1. package com.guowl.testserver;  
  2.   
  3. import io.netty.bootstrap.Bootstrap;  
  4. import io.netty.channel.ChannelFuture;  
  5. import io.netty.channel.ChannelInitializer;  
  6. import io.netty.channel.ChannelOption;  
  7. import io.netty.channel.EventLoopGroup;  
  8. import io.netty.channel.nio.NioEventLoopGroup;  
  9. import io.netty.channel.socket.SocketChannel;  
  10. import io.netty.channel.socket.nio.NioSocketChannel;  
  11.   
  12. public class HelloClient {  
  13.     public void connect(String host, int port) throws Exception {  
  14.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  15.   
  16.         try {  
  17.             Bootstrap b = new Bootstrap();  
  18.             b.group(workerGroup);  
  19.             b.channel(NioSocketChannel.class);  
  20.             b.option(ChannelOption.SO_KEEPALIVE, true);  
  21.             b.handler(new ChannelInitializer<SocketChannel>() {  
  22.                 @Override  
  23.                 public void initChannel(SocketChannel ch) throws Exception {  
  24.                     ch.pipeline().addLast(new HelloClientIntHandler());  
  25.                 }  
  26.             });  
  27.   
  28.             // Start the client.  
  29.             ChannelFuture f = b.connect(host, port).sync();  
  30.   
  31.             // Wait until the connection is closed.  
  32.             f.channel().closeFuture().sync();  
  33.         } finally {  
  34.             workerGroup.shutdownGracefully();  
  35.         }  
  36.   
  37.     }  
  38.   
  39.     public static void main(String[] args) throws Exception {  
  40.         HelloClient client = new HelloClient();  
  41.         client.connect("127.0.0.1", 8000);  
  42.     }  
  43. }  

4、HelloClientIntHandler代码如下:

[java] view plain copy
  1. package com.guowl.testserver;  
  2.   
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.channel.ChannelInboundHandlerAdapter;  
  6.   
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. public class HelloClientIntHandler extends ChannelInboundHandlerAdapter {  
  11.     private static Logger logger = LoggerFactory.getLogger(HelloClientIntHandler.class);  
  12.   
  13.     // 接收server端的消息,并打印出来  
  14.     @Override  
  15.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
  16.         logger.info("HelloClientIntHandler.channelRead");  
  17.         ByteBuf result = (ByteBuf) msg;  
  18.         byte[] result1 = new byte[result.readableBytes()];  
  19.         result.readBytes(result1);  
  20.         System.out.println("Server said:" + new String(result1));  
  21.         result.release();  
  22.     }  
  23.   
  24.     // 连接成功后,向server发送消息  
  25.     @Override  
  26.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
  27.         logger.info("HelloClientIntHandler.channelActive");  
  28.         String msg = "Are you ok?";  
  29.         ByteBuf encoded = ctx.alloc().buffer(4 * msg.length());  
  30.         encoded.writeBytes(msg.getBytes());  
  31.         ctx.write(encoded);  
  32.         ctx.flush();  
  33.     }  
  34. }  


通过上面简单的实例可以发现:

1、在没有任何encoder、decoder的情况下,Netty发送接收数据都是按照ByteBuf的形式,其它形式都是不合法的。

2、接收发送数据操作都是通过handler实现的,handler在netty中占据了非常重要的位置。

3、netty的handler是基于事件触发的,例如当client连接server成功后,client中的HelloClientIntHandler的channelActive方法会自动调用。