Netty(3)-实现客户端与服务端的双向通信

本文详细介绍了使用Netty实现客户端与服务端双向通信的过程。包括基本思路、通信流程及代码实现,展示了如何通过责任链模式处理连接数据的读写逻辑。

一.基本思路

读写逻辑处理均是启动阶段通过给逻辑处理链Pipeline添加逻辑处理器实现连接数据的读写逻辑。

  1. 客户端连接成功回调逻辑处理器的channelActive()方法

  2. 客户端和服务端接收到对方的数据回调用各自逻辑处理器的channelRead()方法。

  3. 客户端和服务端向对方写数据调用writeAndFlush()方法

  4. 客户端和服务端交互的二进制数据传输载体为ByteBuf

二.通信流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxx6KkVY-1580725870430)(http://note.youdao.com/yws/res/9840/76DC2AB9464A4D1882346E496B1C9518)]

三.代码实现

  • NIOServer
/**
 * @Auther: ARong
 * @Description: 服务端-客户端双向通信,服务端在接收到客户端信息后向客户端发出响应
 */
public class NIOServer {
    public static void main(String[] args) {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(); // 监听组
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(); // 工作组

        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class) //NIO模式
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    // 初始化channel
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new FirstServerHandler());
                    }
                }).bind(8000);
    }
}

  • NIOClient
/**
 * @Auther: ARong
 * @Description: 服务端-客户端双向通信,客户端向服务端发送信息
 */
public class NIOClient {
    public static void main(String[] args) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        NioEventLoopGroup group = new NioEventLoopGroup();

        bootstrap.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                // 责任链模式,添加第一次连接的客户端处理逻辑
                ch.pipeline().addLast(new FirstClientHandler());
            }
        });

        Channel channel = bootstrap.connect("127.0.0.1", 8000).channel();
        String message = String.format("HelloWorld From %s", new SimpleDateFormat("hh:mm:ss").format(new Date()));
        channel.writeAndFlush(message);
    }
}
  • FirstServerHandler
/**
 * @Auther: ARong
 * @Description: 服务端被首次连接的处理逻辑
 */
public class FirstServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    /*
     * @Author ARong
     * @Description 接收到服务端消息时触发
     * @Param [ctx, msg]
     * @return void
     **/
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println(String.format("服务端读取到数据%s From %s",
                byteBuf.toString(Charset.forName("utf-8")),
                getCurTime()));

        ByteBuf byteBuf1 = getByteBuf(ctx);
        ctx.channel().writeAndFlush(byteBuf1);
    }

    /*
     * @Author ARong
     * @Description 获取二进制抽象 ByteBuf
     * @Param [ctx]
     * @return io.netty.buffer.ByteBuf
     **/
    private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
        // 获取二进制抽象 ByteBuf
        ByteBuf buffer = ctx.alloc().buffer();

        // 准备数据,指定字符串的字符集为 utf-8
        String response = String.format("服务端返回HelloWorld From %s", getCurTime());
        byte[] bytes = response.getBytes(Charset.forName("utf-8"));

        // 填充数据到 ByteBuf
        buffer.writeBytes(bytes);

        return buffer;
    }

    private String getCurTime() {
        return new SimpleDateFormat("hh:mm:ss").format(new Date());
    }
}
  • FirstClientHandler
/**
 * @Auther: ARong
 * @Description: 首次连接的客户端处理逻辑
 */
public class FirstClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    /*
     * @Author ARong
     * @Description 客户端与服务端首次连接的处理逻辑
     * @Param [ctx]
     * @return void
     **/
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 编码数据
        ByteBuf byteBuf = getByteBuf(ctx);
        // 写回给服务端
        ctx.channel().writeAndFlush(byteBuf);
    }

    @Override
    /*
     * @Author ARong
     * @Description 接收到服务端消息时触发
     * @Param [ctx, msg]
     * @return void
     **/
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println(String.format("客户端读取到数据%s From %s",
                byteBuf.toString(Charset.forName("utf-8")),
                getCurTime()));
    }

    /*
     * @Author ARong
     * @Description 获取二进制抽象 ByteBuf
     * @Param [ctx]
     * @return io.netty.buffer.ByteBuf
     **/
    private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
        // 获取二进制抽象 ByteBuf
        ByteBuf buffer = ctx.alloc().buffer();

        // 准备数据,指定字符串的字符集为 utf-8
        String response = String.format("客户端返回HelloWorld From %s", getCurTime());
        byte[] bytes = response.getBytes(Charset.forName("utf-8"));

        // 填充数据到 ByteBuf
        buffer.writeBytes(bytes);

        return buffer;
    }

    private String getCurTime() {
        return new SimpleDateFormat("hh:mm:ss").format(new Date());
    }
}

依次启动客户端和服务端,实现双向通信:

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BoringRong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值