Netty学习篇(三)--- 客户端 服务端实现双向通信

客户端-服务端:客户端发送数据到服务端

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

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

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

// 设置线程组
 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代码如下:

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都只需要着这样的格式才行,下面开始学习服务端如何读取到这端数据。

服务端-客户端:服务端读取客户端数据

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

.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);
            }
        });
    }
})

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

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()方法,当客户端连接成功并发送数据之后这个方法被自动回调。

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

服务端-客户端:服务端向客户端回应数据

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

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;
    }

}

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

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

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

CSDN地址:https://blog.csdn.net/ZBylant
微信公众号 在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值