Netty 学习(一)

一、什么是Netty

Netty是一个NIO服务框架,简化了网咯应用框架的开发难度。Netty是一个事件驱动模型,(将很多阶段抽象成一个个的事件,让后将事件映射到多个回调方法上)。主要的特点有(https://netty.io/index.html):

  • 为BIO/NIO提供统一的API
  • 是一个灵活的、可扩展的事件驱动模型
  • 提供高度可定制化的线程模型
  • 支持真正的无连接数据报传输
  • 提供高吞吐、低延时,并且占用更少的资源
  • 提供最小化的不必要内存拷贝
  • 提供对proto Buffer的支持

二、Netty 的初级使用

public class TestServer{
    public static void main(String[] args){
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, wokerGroup)
            .channel(NioServerSockerChannel.class)
            .childHandler(new TestServerInitializer());
        ChannelFuture future = bootstrap.bind(8899).sync();
        future.channel().closeFuture().sync();
    }
}
// 自定义启动初始化器
public class TestServerInitializer extens ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) throws Exception{
        ChannelPipeline pipeline = ch.pipiline();
        // HttpServerCodec是HttpRequestDecoder和HttpResponseDecoder的组合
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        pipeline.addLast("serverHandler", new TestServerHandler());
    }
}

// 实现自己的回调方法
public class TestServerHandler extends SimpleChannelInboundHandler<HttpObject>{
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg){
        if(msg instanceof HttpObject){
            ByteBuf content = Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8);
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
            // response.headers().set()
            ctx.writeAndFlush(response);
        }
    }
}
public class MyClient{
    public static void main(String[] args){
        EventLoopGroup loop = new NioEventLoopGroup();
        try{
            Bootstrap strap = new Bootstrap();
            strap.group(loop).channel(NioSocketChannel.class)
                .handler(new MyClientHandler());
            ChannelFuture fut = strap.connect("localhost", 8899).sync();
            fut.channel.closeFuture().sync();
        }finally{
            loop.shutdownGracefull();
        }
    }
}

public class MyClientInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) throws Exception{
        ChannelPipeline pipeline = ch.pipiline();
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast("my client handler", new MyClientHandler());
    }
}

public class MyClientHandler extends SimpleChannelInboundHandler<String>{
    @Overrie
    protected void channelRead0(ChannelHandlerContext ctx, String msg){
        // 服务端向客户端传递数据时,客户端的处理逻辑
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        cause.printStackTrace();
        ctx.close();
    }

    @Overrie
    protected void channelActive(ChannelHandlerContext ctx){
        // 当与服务端建立好连接后,操作
        ctx.writeAndFlush("test");
    }
}

2.1 SimpleChannelInboundHandler

处理流入channel的事件处理,常见的触发有

  • channelActive() channel是活跃状态
  • channelRegisterd()  注册channel
  • channelAdded();    添加channel
  • channelInactive()  非活跃
  • channelUnregistered()  卸载channel

执行顺序是:channelAdded() ->registered()->active()->inactive()->unregistered

2.2 ChannelInitializer

// 自定义启动初始化器
public class TestServerInitializer extens ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) throws Exception{
        ChannelPipeline pipeline = ch.pipiline();
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast("my handler", new MyHandler());
    }
}
// 这里的泛型主要依据是请求的是什么样的格式数据
public class MyHandler extends SimpleChannelInboundHandler<String>{
    // 收到消息后的处理逻辑
    @Overrie
    protected void channelRead0(ChannelHandlerContext ctx, String msg){

    }
}

三、聊天室实例

需求:

第一个客户端A与服务端建立连接后,服务端打印A上线;

后续的客户端与服务端建立好连接后,服务端打印X上线,并且广播X上线给已经建立好的连接。

客户端X发送一条消息后,广播到其余客户端

如果客户端下线后,服务端向所有的客户端广播X下线。

// 核心思想,利用channel的事件机制,将对应建立好的channel加入到一个组里维护
public class MyChatServerHandler extends SimpleChannelInboundHandler<String>{
    private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    
    @Override
    public void channelRead0(ChannelHandlerContext ctx, String msg){
        Channel channel = ctx.channel();
        group.forEach(t -> {
            if(channel != t){
                t.writeAndFlush("非自己发出的消息");
            }else{
                t.writeAndFlush("自己发出的消息");
            }
        });
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx){
        Channel ch = ctx.channel();
        group.writeAndFlush(ch.remoteAddress() +"online");
        group.add(ch);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx){
        Chanel channel = ctx.channel();
        group.writeAndFlush(channel.remoteAddress() + "offline");
        // group.remove(channel);  这个netty会自动调用,所以可以省略
    }
    
    @Override
    public void channelActive(ChannelHandlerContext ctx){
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + "上线");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx){
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + "下线");
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值