NettyServer fe

package com.jtc.test;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;

@Slf4j
public class NettyServerFe {
    public static void main(String[] args) {

        //EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件
        //EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,
        // 后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
        NioEventLoopGroup boss = new NioEventLoopGroup(1);     //专门处理 accept 事件
        NioEventLoopGroup worker = new NioEventLoopGroup();   //服务器端 NIO worker 工人,处理 read 事件
        //打印日志,对入站/出站事件进行日志记录
        LoggingHandler LOGGIN_HANDLER = new LoggingHandler(LogLevel.DEBUG);

        try {
            //服务端ServerBootstrap,客户端Bootstrap
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            //在客户端建立连接时,如果在指定毫秒内无法连接,会抛出 timeout 异常
            serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000);
            //选择服务 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现
            serverBootstrap.channel(NioServerSocketChannel.class);

            serverBootstrap.group(boss, worker);

            //下面添加的处理器都是给 SocketChannel 用的,而不是给 ServerSocketChannel。ChannelInitializer 处理器(仅执行一次),
            // 它的作用是待客户端 SocketChannel 建立连接后,执行 initChannel 以便添加更多的处理器
            serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {

                    //SocketChannel 的处理器,解码 ByteBuf => String
                    //ch.pipeline().addLast(new StringDecoder());

                    ch.pipeline().addLast(LOGGIN_HANDLER);

                    // 用来判断是不是 读空闲时间过长,或 写空闲时间过长
                    // 45s 内如果没有收到 channel 的数据,会触发一个 IdleState#READER_IDLE 事件
                    ch.pipeline().addLast(new IdleStateHandler(45, 0, 0));
                    // ChannelDuplexHandler 可以同时作为入站和出站处理器
                    ch.pipeline().addLast(new ChannelDuplexHandler() {
                        // 用来触发特殊事件
                        @Override
                        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
                            IdleStateEvent event = (IdleStateEvent) evt;
                            // 触发了读空闲事件
                            if (event.state() == IdleState.READER_IDLE) {
                                log.debug("已经 45s 没有读到数据了");
                                ctx.channel().close();
                            }
                        }
                    });

                    //业务处理器,使用上一个处理器的处理结果
                    //ChannelInboundHandlerAdapter入站处理器,主要用来读取客户端数据,写回结果
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        //ctx 里包含着ChannelHandler中的上下文信息,功能就是用来管理它所关联的ChannelHandler和在同一个ChannelPipeline中ChannelHandler的交互。
                        //把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBuf
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            ByteBuf buffer = (ByteBuf) msg;
                            System.out.println(buffer.toString(Charset.defaultCharset()));

                            // 建议使用 ctx.alloc() 创建 ByteBuf,其引用会计数 + 1
                            ByteBuf response = ctx.alloc().buffer();
                            //写入 netty 的 ByteBuf
                            response.writeBytes(buffer);
                            ctx.writeAndFlush(response);
                            //将 ByteBuf 对象的引用计数减 1
                            response.release();

                            //把msg传给下一个处理器加工(下一个为出站处理器)
                            //ctx.channel().write(msg);
                        }
                    });

                    //ChannelOutboundHandlerAdapter出站处理器,主要对写回结果进行加工
                    // ch.pipeline().addLast(new ChannelOutboundHandlerAdapter(){
                    //     @Override
                    //     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                    //         super.write(ctx, msg, promise);
                    //     }
                    // });
                }
            });
            //ServerSocketChannel 绑定的监听端口
            Channel channel = serverBootstrap.bind(8080).sync().channel();
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("server error", e);
        }finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值