第一个Netty程序(编写Hello Netty服务器)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fu123123fu/article/details/85170691

具体步骤

  • 构建一对主从线程组
  • 定义服务器启动类
  • 为服务器设置channel
  • 设置处理从线程池的助手类初始化器(我们会有一个channel,这个channel会有一堆相应的助手类handler对它进行处理,如编解码处理、读写数据等。这些操作都是需要归类在助手类的初始化器里面,简单来说它就是一个类,在这个类里要添加很多的助手类。)
  • 监听启动和关闭服务器

设置channel初始化器

每一个channel由多个handler共同组成管道(pipeline)。
在下图,左侧有一个channel,右侧是一个pipeline。当这个channel注册完之后,就会有一个pipeline,其实就是一个初始化器。在pipeline里面,我们需要为它设置很多handler,这些handler我们可以称之为是一个个的助手类,这些助手类就会针对channel去做一些处理。比如现在有个管道通了,就会分别去注册handlerA、hanlderB、handlerC。当客户端和服务端进行交互的时候,相应的助手类会针对我们的请求去做相应的处理。也可以把pipeline当成是一个大的拦截器,然后里面有很多小的拦截器handler,当请求过来的时候,会一层一层的去拦截。
在这里插入图片描述

代码实现

1、构建服务器(HelloServer.java)

/**
 * 创建人:taofut
 * 创建时间:2018-12-18 21:50
 * 实现客户端发送一个请求,服务器会返回hell netty
 */
public class HelloServer {

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

        //定义一对线程组
        //主线程组,用于接收客户端的请求,但是不做任何处理,跟老板一样,不做事
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        //从线程组,等待老板线程组安排任务,然后干活
        EventLoopGroup workGroup=new NioEventLoopGroup();

        try {
            //netty服务器的创建 ServerBootstrap是一个启动类
            ServerBootstrap bootstrap=new ServerBootstrap();
            bootstrap.group(bossGroup,workGroup)            //设置主从线程组
                    .channel(NioServerSocketChannel.class)  //设置nio的双向通道
                    .childHandler(new HelloServerInitializer());                    //子处理器,用于处理workGroup

            //启动server,并且设置端口号为8088,同时启动方式为同步
            ChannelFuture channelFuture=bootstrap.bind(8088).sync();

            //监听关闭的channel,设置为同步方式
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭主线程
            bossGroup.shutdownGracefully();
            //关闭从线程
            workGroup.shutdownGracefully();
        }
    }
}

2、创建channel初始化器(HelloServerInitializer.java)

/**
 * 创建人:taofut
 * 创建时间:2018-12-19 20:22
 * 描述:初始化器,channel注册后,会执行里面相应的初始化方法
 */
public class HelloServerInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        //通过SocketChannel获得对应的管道
        ChannelPipeline pipeline=socketChannel.pipeline();

        //通过管道,添加handler
        //HttpServerCodec是由netty官方提供的助手类,可以理解为拦截器
        //当请求到服务端,我们需要做解码,响应到客户端做编码
        pipeline.addLast("HttpServerCodec",new HttpServerCodec());

        //添加自定义的助手类,返回 "hello netty"
        pipeline.addLast("customHandler",new CustomHandler());
    }

}

3、添加自定义助手类handler(CustomHandler.java)

/**
 * 创建人:taofut
 * 创建时间:2018-12-19 20:48
 * 描述:创建自定义助手类
 */
//SimpleChannelInboundHandler:对于请求来讲,相当于入站、入境
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject>{

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject)
            throws Exception {
        //获取channel
        Channel channel=channelHandlerContext.channel();

        if(httpObject instanceof HttpRequest){
            //显示客户端的远程地址
            System.out.println(channel.remoteAddress());

            //定义发送的数据消息
            ByteBuf content= Unpooled.copiedBuffer("hello netty", CharsetUtil.UTF_8);

            //构建一个http response
            FullHttpResponse response=
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                            HttpResponseStatus.OK,
                            content);

            //为响应增加数据类型和长度
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());

            //把响应刷到客户端
            channelHandlerContext.writeAndFlush(response);
        }

    }
}

运行测试

启动HelloServer.java里的main方法,然后在浏览器输入地址:http://localhost:8088/, “hello netty”被打印出来。
在这里插入图片描述

Channel生命周期

将以上的CustomHandler.java类里面的channel对应的方法一一实现。

/**
 * 创建人:taofut
 * 创建时间:2018-12-19 20:48
 * 描述:创建自定义助手类
 */
//SimpleChannelInboundHandler:对于请求来讲,相当于入站、入境
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject>{

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject)
            throws Exception {
        //获取channel
        Channel channel=channelHandlerContext.channel();

        if(httpObject instanceof HttpRequest){
            //显示客户端的远程地址
            System.out.println(channel.remoteAddress());

            //定义发送的数据消息
            ByteBuf content= Unpooled.copiedBuffer("hello netty", CharsetUtil.UTF_8);

            //构建一个http response
            FullHttpResponse response=
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                            HttpResponseStatus.OK,
                            content);

            //为响应增加数据类型和长度
            response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());

            //把响应刷到客户端
            channelHandlerContext.writeAndFlush(response);
        }

    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel注册...】");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel移除...】");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel活跃...】");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel不活跃...】");
        super.channelInactive(ctx);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel数据读取完毕...】");
        super.channelReadComplete(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("【用户事件触发...】");
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【channel可写更改...】");
        super.channelWritabilityChanged(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("【捕获异常...】");
        super.exceptionCaught(ctx, cause);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【助手类添加...】");
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("【助手类移除...】");
        super.handlerRemoved(ctx);
    }
}

然后,我们继续访问服务器,观察控制台的打印结果。这里,我使用了linux命令代替浏览器发起请求。(浏览器访问会发起额外的不相关请求,为了保证结果清晰,才使用linux命令代替浏览器。)
输入命令:curl 192.168.3.101(本机IP):8088
控制台结果:

【助手类添加…】
【channel注册…】
【channel活跃…】
/192.168.3.101:53147
【channel数据读取完毕…】
【channel数据读取完毕…】
【channel不活跃…】
【channel移除…】
【助手类移除…】

猜你喜欢

转载自blog.csdn.net/fu123123fu/article/details/85170691