前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Netty】客户端和服务端实现双向通信

【Netty】客户端和服务端实现双向通信

作者头像
周三不加班
发布2019-09-04 10:08:49
3K0
发布2019-09-04 10:08:49
举报
文章被收录于专栏:程序猿杂货铺程序猿杂货铺

时间,犹如白驹过隙,一眨眼,国庆假期已经过去了四天了,日子总是像指间流过的细沙,在不经意间悄然滑落。所以,你该开始学Netty了

,今天开始学习如何实现服务端和客户端的双向通信,毕竟和聊天一样,要有来有回才有意思

1

客户端发送数据到服务端

上篇文章提到,读写处理逻辑是在 Bootstraphandler()方法指定的,上节课写的如下代码:

代码语言:javascript
复制
.handler(new ChannelInitializer<Channel>() {
         @Override
         protected void initChannel(Channel channel) {
             // 指定数据读写处理逻辑 
             channel.pipeline().addLast(new StringEncoder());
         }
});

现在,我们自定义一段处理逻辑给它,如下:

代码语言:javascript
复制
// 设置线程组
 bootstrap.group(group)
      // 设置线程模型
      .channel(NioSocketChannel.class)
      // 设置连接读写处理逻辑
      .handler(new ChannelInitializer<Channel>() {
              @Override
              protected void initChannel(Channel channel) {
                   // channel.pipeline() 责任链模式 返回和这条连接相关的逻辑处理链
                   // addLast 添加一个逻辑处理器 也就是我们自定义的读写处理逻辑了
                    channel.pipeline().addLast(new CustomizeHandler());
               }
      });

上述代码中的

  • channel.pipeline()责任链模式 返回和这条连接相关的逻辑处理链
  • addLast() 添加一个逻辑处理器 也就是我们自定义的读写处理逻辑了

其中,CustomizeHandler.java代码如下:

代码语言:javascript
复制
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * Created by zhoudl on 2018/10/4.
 */
public class CustomizeHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(new Date() + ": 客户端开始写数据");
        // 1. 获取数据
        ByteBuf buffer = getByteBuf(ctx);
        // 2. 写数据
        ctx.channel().writeAndFlush(buffer);
    }

    private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
        // 1. 获取二进制抽象 ByteBuf
        ByteBuf buffer = ctx.alloc().buffer();
        // 2. 准备数据,指定字符串的字符集为 utf-8
        byte[] bytes = "你好,苍穹盛夏童鞋!".getBytes(Charset.forName("utf-8"));
        // 3. 填充数据到 ByteBuf
        buffer.writeBytes(bytes);
        return buffer;
    }

}

继承自ChannelInboundHandlerAdapterchannelActive()方法会在连接成功之后自动回调;

写数据的过程分为两部分:

  • 获取一个ByteBuf格式的二进制数据,这个结构是Netty对二进制数据做的抽象;
  • ctx.alloc()学过C/C++ 的人肯定知道 alloc 和内存相关,所以这行代码的意思是获取ByteBuff的内存管理器,而这个内存管理器的作用就是分配一个ByteBuff出来;
  • 填充数据到ByteBuff中,这样就达到了Netty传输数据的要求;
  • 使用ctx.channel().writeAndFlush(buffer);将数据写出到服务端。

上述代码和传统的Java Socket编程不同的一点就是写出的数据格式不同,Netty是自己对二进制数据做了一层抽象,定义了一个ByteBuff的结构出来,无论数据读还是写,Netty都只需要着这样的格式才行,下面开始学习服务端如何读取到这端数据。

2

服务端读取客户端数据

同理,服务端的读写处理逻辑处理还是在ServerBootstrapchildHandler()方法中,这里除了单词不同之外,其他和客户端同理,这就是Netty API 友好的体现方式之一,学了客户端,服务端猜也能猜个大概,所谓大胆猜测,小心验证。

代码语言:javascript
复制
.childHandler(new ChannelInitializer<Channel>() {
    @Override
    protected void initChannel(Channel channel) throws Exception {
        channel.pipeline().addLast(new StringDecoder());
        channel.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
            @Override
            protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                System.out.println(msg);
            }
        });
    }
})

一样的,接下来我们自定义一个处理器出来,代码如下:

代码语言:javascript
复制
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * Created by zhoudl on 2018/10/4.
 */
public class CustomizeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println(new Date() + ": 服务端读取数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
    }
}

同样的,你会发现,它继承了ChannelInboundHandlerAdapter,不同的在于这里是读数据,所以覆盖的方法变了,换成了read()方法,当客户端连接成功并发送数据之后这个方法被自动回调。

接下来开始学习服务端向客户端回应数据的过程,学完上边这俩之后,现在应该已经没什么难度了。

3

服务端向客户端回应数据

此处写数据和客户端写数据过程类似,我就不再赘述了,直接上代码,简单直接又明了,代码如下:

代码语言:javascript
复制
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * Created by zhoudl on 2018/10/4.
 */
public class CustomizeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        System.out.println(new Date() + ": 服务端读取数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
        // 回复数据到客户端
        System.out.println(new Date() + ": 服务端写出数据");
        ByteBuf out = getByteBuf(ctx);
        ctx.channel().writeAndFlush(out);
    }

    private ByteBuf getByteBuf(ChannelHandlerContext ctx) {
        byte[] bytes = "你好,我是苍穹盛夏!".getBytes(Charset.forName("utf-8"));
        ByteBuf buffer = ctx.alloc().buffer();
        buffer.writeBytes(bytes);
        return buffer;
    }

}

4

标题内容

紧接着,客户端需要读取服务端发过来的数据,而读取数据的过程和上述服务端读取客户端数据的代码无异,将以下代码添加到CustomizeHandler中,便能实现客户端读数据的逻辑,代码如下:

代码语言:javascript
复制
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf byteBuf = (ByteBuf) msg;
    System.out.println(new Date() + ": 客户端读到数据 -> " + byteBuf.toString(Charset.forName("utf-8")));
}

4

总结

  • Netty数据交换传输的载体是ByteBuff,只有把数据填充到ByteBuff之后才能使得对端接收成功数据;
  • ByteBuff是通过连接的内存管理器创建的;
  • 写数据的方法是writeAndFlush;
  • 逻辑处理链`pipeline `,使用该方法添加逻辑处理器,具体的深层次原理以后再讨论,今天先学到这里。

关于ByteBuff的学习会单独写一篇文章,因为内容比较多!

以上代码会同步更新在本人的Github和CSDN上

Github地址:https://github.com/Bylant/LeetCode

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员啊粥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档