正井猫

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Netty系列入门之HelloWorld(一)

一. 简介

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.

'Quick and easy' doesn't mean that a resulting application will suffer from a maintainability or a performance issue. Netty has been designed carefully with the experiences earned from the implementation of a lot of protocols such as FTP, SMTP, HTTP, and various binary and text-based legacy protocols. As a result, Netty has succeeded to find a way to achieve ease of development, performance, stability, and flexibility without a compromise.

  上面一段话引子Netty官网:Netty是一个基于NIO的客户端与服务端框架,可以让我们简单快速的开发协议化的客户端/服务端的网络应用。Netty可以非常简单的进行网络编程,例如TCP和UDP的套接字服务。"快速和简单"并不意味着我们使用Netty编写的应用很难维护,也不会导致性能问题。Netty的设计非常的谨慎,设计Netty的经验来源于对FTP, SMTP,HTTP以及遗留的各种二进制和基于文本的协议的大量的实现。总之,Netty在实现了快速快发,高性能,稳定性强,易于扩展方面寻找到一种极佳的方式,而不会为了某些因素作出任何的妥协。

  当然,以上只是官网的陈述,笔者发现几乎所有的框架都会使用"简单"、"易于使用"这类的词语,然而笔者在学习Netty的过程中并不这么认为,可能是笔者智商有限吧。那么多的不说了,按照笔者一贯的风格,我们就直接上代码吧。

二. Netty入门之HelloWorld

  对于Netty来说,编写一个"Hello World"程序并不是那么的简单,对于初学Netty的人来讲,可能觉得异常的繁琐,怎么说了,繁琐也得学呀,谁让咱走上了编程这条不归路呢?

2.1 Netty的启动服务程序

public class ServerTest {
    public static void main(String[] args) throws InterruptedException {
        /**
         * bossGroup, 父类的事件循环组只是负责连接,获取到连接后交给 workergroup子的事件循环组,
         * 参数的获取,业务的处理等工作均是由workergroup这个子事件循环组来完成,一个事件循环组一样
         * 可以完成所有的工作,但是Netty推荐的方式是使用两个事件循环组。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();  //创建父事件循环组
        EventLoopGroup workerGroup = new NioEventLoopGroup(); //创建子类的事件循环组
        
        try{
            //创建启动服务器的对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();  
            
            /**
             * group方法接收两个参数, 第一个为父时间循环组,第二个参数为子事件循环组
             */
            serverBootstrap.group(bossGroup, workerGroup)
                           .channel(NioServerSocketChannel.class)  //bossGroup的通道,只是负责连接
                           .childHandler(new TestChannelnitializer()); //workerGroup的处理器,
            
            ChannelFuture channelFuture = serverBootstrap.bind(8899).sync();  //绑定端口
            channelFuture.channel().closeFuture().sync();
            
        }finally{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2.2 通道初始化程序

  通道的初始化程序主要是为workerGroup添加各种Handler.

/**
 * 初始化一个通道,主要用于设置各种Handler
 */
public class TestChannelnitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
         /**
         * Handler就相当于Servlet中的过滤器, 请求和响应都会走Handler
         * HttpServerCodec: http的编解码器,用于Http请求和相应
         */
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());
    }
}

2.3 自定义的Handler

/**
 * 自定义处理器 
 */
public class TestHttpServerHandler extends SimpleChannelInboundHandler<HttpObject>{

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if(msg instanceof HttpRequest){
            //要返回的内容, Channel可以理解为连接,而连接中传输的信息要为ByteBuf
            ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
            
            //构造响应
            FullHttpResponse response = 
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
            
            //设置头信息的的MIME类型
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");  //内容类型
            //设置要返回的内容长度
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); //内容长度
            //将响应对象返回
            ctx.writeAndFlush(response);
        }
    }
    
    //通道注册成功
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel register...");
        super.channelRegistered(ctx);
    }
    
    /**
     * 自定义的Handler被添加,也就是在TestChannelnitializer的initChannel方法中,
     * pipeline.addLast("testHttpServerHandler", new TestHttpServerHandler());
     * 这行代码执行的时候,该方法被触发
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handler added...");
        super.handlerAdded(ctx);
    }
    
    //通道处于活动状态,即可用状态
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel active...");
        super.channelActive(ctx);
    }
    
    //通道处于不活动状态
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel inactive...");
        super.channelInactive(ctx);
    }
    
    //通道取消注册
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel unregister...");
        super.channelUnregistered(ctx);
    }
}

三.运行

  首先启动ServerTest的主函数,然后再网页中输入: http://localhost:88899,就会出现你希望看到的东西(前提是代码要写对),如下图:

四.总结

  很多人可能看到该代码会觉得头昏脑胀,谁不是呢?笔者对于编程的学习方式一直都是:不懂就敲,先学会用,还是不懂,接着敲,直到敲到你认为很多东西理所当然了,就是会了。

 

posted on 2017-06-12 00:52  正井猫  阅读(4410)  评论(3编辑  收藏  举报