一個羣聊的netty 例子

翻了翻《Netty 實戰》,看了些概念,仍是以爲似是而非,因而寫了些代碼,通道啥的都建得挺好。可是channelRead0一直收不到消息。
後來東摸西摸,發現我client 建立channel後立刻發消息是不對的,此時channel尚未徹底建好,致使服務端收不到消息。改爲通道active後,server 給 client發送消息,client 收到消息後纔開始向server發消息,此時sever是能收到的。bootstrap

實現Server handler

@Slf4j
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {

    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // 處理handler 添加,全局保存channel
        log.info("add a handler");
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
        }
        channels.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 處理handler 刪除,全局刪除channel
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 離開\n");
        }
        channels.remove(ctx.channel());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel incoming = ctx.channel();

        log.info("收到消息: " + msg);
        for (Channel channel : channels) {
            if (channel != incoming) {
                // 羣發給其餘用戶
                channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + msg + "\n");
            } else {
                // 迴應當前用戶
                channel.writeAndFlush("[響應]" + msg + "\n");
            }
        }
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 通道激活,通知client,能夠準備通訊了
        Channel incoming = ctx.channel();
        log.info("ChatClient:" + incoming.remoteAddress() + "在線");
        incoming.writeAndFlush("welcome" + "\n");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 通道失效,client 掉線
        Channel incoming = ctx.channel();
        log.info("ChatClient:" + incoming.remoteAddress() + "掉線");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception 
    {
        Channel incoming = ctx.channel();

        // 當出現異常就關閉鏈接
        log.info("ChatClient:" + incoming.remoteAddress() + "異常");
        cause.printStackTrace();
        ctx.close();
    }
}

服務端經過引導綁定IP

ServerBootstrap serverBootstrap = new ServerBootstrap();

        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(
                        new ChannelInitializer<NioSocketChannel>() {
                            @Override
                            protected void initChannel(NioSocketChannel ch) throws Exception 
                            {
                                ChannelPipeline pipeline = ch.pipeline();
                                pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                                pipeline.addLast("decoder", new StringDecoder());
                                pipeline.addLast("encoder", new StringEncoder());
                                pipeline.addLast("handler", new ChatServerHandler());
                                log.info("ChatClient:" + ch.remoteAddress() + "鏈接上");
                            }
                        }
                ).option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .bind(18169);

實現 client handler

@Slf4j
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        log.info(msg);
        Channel ch = ctx.channel();
        
        // 收到welcome後,給server發送消息
        if(msg.startsWith("welcome")){
            ch.writeAndFlush("hello world!" + "\r\n");
            ch.writeAndFlush("wish you happy!" + "\r\n");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception 
    {
        cause.printStackTrace();
        //        出現異常時關閉鏈接
        ctx.close();
    }
}

經過引導建立 client channel

Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();

        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(
                        new ChannelInitializer<NioSocketChannel>() {
                            @Override
                            protected void initChannel(NioSocketChannel ch) throws Exception 
                            {
                                ChannelPipeline pipeline = ch.pipeline();
                                // 解決粘包問題
                                pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                                pipeline.addLast("decoder", new StringDecoder());
                                pipeline.addLast("encoder", new StringEncoder());
                                pipeline.addLast("handler", new ChatClientHandler());
//                                log.info("ChatServer:" + ch.remoteAddress() + "鏈接上");
                            }
                        }
                ).option(ChannelOption.SO_KEEPALIVE, true);

        Channel channel = bootstrap.connect("127.0.0.1", 18169).channel();

    }
相關文章
相關標籤/搜索