一起学Netty(一)之 Hello Netty

   四月份花了大概一个月的时间翻译了2015年12月出版的《Netty in Action》 说实话,翻译完了,感觉只是对Netty有了一些初步的了解,对Netty的模型在脑海中也是有了初步的雏形,好记性不如烂笔头,这两个月就准备系统的学习一下Netty,学习到什么程度,我自己也不清楚了,反正会记录下学习的点点滴滴的


按照惯例,我们学习任何新的知识的时候,总是来一个Hello world的demo


hello world版的demo,我们使用《Netty in action》 中的案例:http://blog.csdn.net/linuu/article/details/51084638


案例说明:客户端发送一个信息到服务器端,服务器端将信息原样返回


maven的依赖,我们如下:

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
	<groupId>io.netty</groupId>
	<artifactId>netty-all</artifactId>
	<version>4.0.21.Final</version>
     </dependency>
  </dependencies>



我们先写Netty器端的ChannelHandler,我们命名Netty的服务端的处理器为:HelloWorldServerHandler,继承于ChannelInboundHandlerAdapter,《Netty in Action》中介绍过,我们不需要使每一个inboundChannel继承于ChannelInboundHandler,这样会需要我们实现ChannelInboundHandler中的所有接口,在一般的channel中我们没有必要这样做,这样只会增加我们的额外的工作量,我们只需要继承ChannelInboundHandlerAdapter,继承它的适配就可以了,我们需要实现几个特别重要的方法,例如读取的方法channelRead和异常处理的方法exceptionCaught,源码如下:

package com.lyncc.netty.hello;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class HelloWorldServerHandler extends ChannelInboundHandlerAdapter{
    
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("server channelRead..");
        System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());
        ctx.write("server write"+msg);
        ctx.flush();
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

接下来我们就要写Netty的服务器端了,书中写的很清楚,我们需要写一个ServerBootstrap,ServerBootstrap的写法是现在比较流行的流式编程法,我们需要指定它的transports,是NIO还是OIO,还需要指定端口号,最最重要的是安装server端的处理器,也就是我们之前写的HelloWorldServerHandler,还有一些Option的配置,这里只是用到,以后我们一起分析这些Option的作用,写server端还有一点需要注意,就是需要关闭连接,释放线程资源,源码如下:

package com.lyncc.netty.hello;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.net.InetSocketAddress;

public class HelloWorldServer {

    private int port;
    
    public HelloWorldServer(int port) {
        this.port = port;
    }
    
    public void start(){
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        
                        protected void initChannel(SocketChannel ch) throws Exception {
//                            ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                            ch.pipeline().addLast("decoder", new StringDecoder());
                            ch.pipeline().addLast("encoder", new StringEncoder());
                            ch.pipeline().addLast(new HelloWorldServerHandler());
                        };
                        
                    }).option(ChannelOption.SO_BACKLOG, 128)   
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
             // 绑定端口,开始接收进来的连接
             ChannelFuture future = sbs.bind(port).sync();  
             
             System.out.println("Server start listen at " + port );
             future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new HelloWorldServer(port).start();
    }
}
如法炮制,客户端的handler,我们依旧命名为HelloWorldClientHandler,也是继承于ChannelInboundHandlerAdapter

package com.lyncc.netty.hello;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class HelloWorldClientHandler extends ChannelInboundHandlerAdapter{
    
  
      @Override
      public void channelActive(ChannelHandlerContext ctx) {
          System.out.println("HelloWorldClientHandler Active");
      }
  
      @Override
      public void channelRead(ChannelHandlerContext ctx, Object msg) {
         System.out.println("HelloWorldClientHandler read Message:"+msg);
      }
  
  
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
         cause.printStackTrace();
         ctx.close();
      }

}
客户端我们需要写一个Bootstrap,也需要指定客户端的处理器,我们这里在客户端和服务器端都加了编码和解码两个String类型的译码器,这样的例子也算比较完整的hello world的例子了,完整的代码如下:

package com.lyncc.netty.hello;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class HelloWorldClient {
    
    static final String HOST = System.getProperty("host", "127.0.0.1");
    static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
    static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));

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

        // Configure the client.
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .option(ChannelOption.TCP_NODELAY, true)
             .handler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline p = ch.pipeline();
                     p.addLast("decoder", new StringDecoder());
                     p.addLast("encoder", new StringEncoder());
                     p.addLast(new HelloWorldClientHandler());
                 }
             });

            ChannelFuture future = b.connect(HOST, PORT).sync();
            future.channel().writeAndFlush("Hello Netty Server ,I am a common client");
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

}
我们打开HelloWorldServer的代码,运行main函数,eclipse的控制台打印信息如下:

说明服务器已经成功运行,在8080端口监听了

我们再打开HelloWorldClient代码,同样运行main函数。控制台打印信息如下:

第一行是HelloWorldClientHandler该处理器的channelActive的方法打印的输出,第二行是channelRead方法的输出,这个输出是服务器端写入的,返回给客户端的


我们再回头看服务器端,此时服务器的HelloWorldServerHandler的channelRead的方法应该打印从客户端收到的信息了


好了,到此为止,Netty的服务器端和客户端就完全搭建完毕了,我们做到了客户端发送信息,服务器端接收且把收到的信息原样返回给客户端了




评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值