通讯协议,指的是把Netty通讯管道中的二进制流转换为对象、把对象转换成二进制流的过程。转换过程追根究底还是ChannelInboundHandler、ChannelOutboundHandler的实现类在进行处理。ChannelInboundHandler负责把二进制流转换为对象,ChannelOutboundHandler负责把对象转换为二进制流。

接下来要构建一个Server,同时支持Person通讯协议和String通讯协议。

  • Person通讯协议:二进制流与Person对象间的互相转换。
  • String通讯协议:二进制流与有固定格式要求的String的相互转换。String格式表示的也是一个Person对象,格式规定为:name:xx;age:xx;sex:xx;
这时候,来自客户端的请求,会依次传递给两个通讯解析接口进行解析,每个通讯接口判断是否是匹配的协议,如果是则进行解析,如果不是则传递给其它通讯接口进行解析。
 
实体类:Person
  1. package com.guowl.testobjcoder;
  2. import java.io.Serializable;
  3. public class Person implements Serializable{
  4. private static final long   serialVersionUID    = 1L;
  5. private String  name;
  6. private String  sex;
  7. private int     age;
  8. public String toString() {
  9. return "name:" + name + " sex:" + sex + " age:" + age;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getSex() {
  18. return sex;
  19. }
  20. public void setSex(String sex) {
  21. this.sex = sex;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. public void setAge(int age) {
  27. this.age = age;
  28. }
  29. }

Server端的类为:Server PersonDecoder StringDecoder BusinessHandler

1、Server 开启Netty服务
  1. package com.guowl.testobjcoder;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. // 测试coder 和 handler 的混合使用
  11. public class Server {
  12. public void start(int port) throws Exception {
  13. EventLoopGroup bossGroup = new NioEventLoopGroup();
  14. EventLoopGroup workerGroup = new NioEventLoopGroup();
  15. try {
  16. ServerBootstrap b = new ServerBootstrap();
  17. b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
  18. .childHandler(new ChannelInitializer<SocketChannel>() {
  19. @Override
  20. public void initChannel(SocketChannel ch) throws Exception {
  21. ch.pipeline().addLast(new PersonDecoder());
  22. ch.pipeline().addLast(new StringDecoder());
  23. ch.pipeline().addLast(new BusinessHandler());
  24. }
  25. }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
  26. ChannelFuture f = b.bind(port).sync();
  27. f.channel().closeFuture().sync();
  28. } finally {
  29. workerGroup.shutdownGracefully();
  30. bossGroup.shutdownGracefully();
  31. }
  32. }
  33. public static void main(String[] args) throws Exception {
  34. Server server = new Server();
  35. server.start(8000);
  36. }
  37. }

2、PersonDecoder  把二进制流转换成Person对象

  1. package com.guowl.testobjcoder;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.handler.codec.ByteToMessageDecoder;
  5. import java.util.List;
  6. import com.guowl.utils.ByteBufToBytes;
  7. import com.guowl.utils.ByteObjConverter;
  8. public class PersonDecoder extends ByteToMessageDecoder {
  9. @Override
  10. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  11. byte n = "n".getBytes()[0];
  12. byte p = in.readByte();
  13. in.resetReaderIndex();
  14. if (n != p) {
  15. // 把读取的起始位置重置
  16. ByteBufToBytes reader = new ByteBufToBytes();
  17. out.add(ByteObjConverter.byteToObject(reader.read(in)));
  18. } else {
  19. // 执行其它的decode
  20. ctx.fireChannelRead(in);
  21. }
  22. }
  23. }

3、StringDecoder 把满足条件的字符串转换成Person对象

  1. package com.guowl.testobjcoder;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.handler.codec.ByteToMessageDecoder;
  5. import java.util.List;
  6. import com.guowl.utils.ByteBufToBytes;
  7. public class StringDecoder extends ByteToMessageDecoder {
  8. @Override
  9. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
  10. // 判断是否是String协议
  11. byte n = "n".getBytes()[0];
  12. byte p = in.readByte();
  13. // 把读取的起始位置重置
  14. in.resetReaderIndex();
  15. if (n == p) {
  16. ByteBufToBytes reader = new ByteBufToBytes();
  17. String msg = new String(reader.read(in));
  18. Person person = buildPerson(msg);
  19. out.add(person);
  20. //in.release();
  21. } else {
  22. ctx.fireChannelRead(in);
  23. }
  24. }
  25. private Person buildPerson(String msg) {
  26. Person person = new Person();
  27. String[] msgArray = msg.split(";|:");
  28. person.setName(msgArray[1]);
  29. person.setAge(Integer.parseInt(msgArray[3]));
  30. person.setSex(msgArray[5]);
  31. return person;
  32. }
  33. }
4、BusinessHandler 展现客户端请求的内容
  1. package com.guowl.testobjcoder;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. public class BusinessHandler extends ChannelInboundHandlerAdapter {
  7. private Logger  logger  = LoggerFactory.getLogger(BusinessHandler.class);
  8. @Override
  9. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  10. Person person = (Person) msg;
  11. logger.info("BusinessHandler read msg from client :" + person);
  12. }
  13. @Override
  14. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  15. ctx.flush();
  16. }
  1. <span style="white-space:pre">    </span>// 解决注意事项1中的问题。
  1. <pre name="code" class="java"><span style="white-space:pre">    </span>@Override
  2. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  3. ctx.close();
  4. }

}

客户端1发送Person格式的协议:Client ClientInitHandler PersonEncoder
1、Client 
  1. package com.guowl.testobjcoder;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioSocketChannel;
  10. public class Client {
  11. public void connect(String host, int port) throws Exception {
  12. EventLoopGroup workerGroup = new NioEventLoopGroup();
  13. try {
  14. Bootstrap b = new Bootstrap();
  15. b.group(workerGroup);
  16. b.channel(NioSocketChannel.class);
  17. b.option(ChannelOption.SO_KEEPALIVE, true);
  18. b.handler(new ChannelInitializer<SocketChannel>() {
  19. @Override
  20. public void initChannel(SocketChannel ch) throws Exception {
  21. ch.pipeline().addLast(new PersonEncoder());
  22. Person person = new Person();
  23. person.setName("guowl");
  24. person.setSex("man");
  25. person.setAge(30);
  26. ch.pipeline().addLast(new ClientInitHandler(person));
  27. }
  28. });
  29. ChannelFuture f = b.connect(host, port).sync();
  30. f.channel().closeFuture().sync();
  31. } finally {
  32. workerGroup.shutdownGracefully();
  33. }
  34. }
  35. public static void main(String[] args) throws Exception {
  36. Client client = new Client();
  37. client.connect("127.0.0.1", 8000);
  38. }
  39. }
2、ClientInitHandler 向服务端发送Person对象
  1. package com.guowl.testobjcoder;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. public class ClientInitHandler extends ChannelInboundHandlerAdapter {
  7. private static Logger   logger  = LoggerFactory.getLogger(ClientInitHandler.class);
  8. private Person person;
  9. public ClientInitHandler(Person person){
  10. this.person = person;
  11. }
  12. @Override
  13. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  14. logger.info("ClientInitHandler.channelActive");
  15. ctx.write(person);
  16. ctx.flush();
  17. }
  18. }
3、PersonEncoder 把Person对象转换成二进制进行传送
  1. package com.guowl.testobjcoder;
  2. import com.guowl.utils.ByteObjConverter;
  3. import io.netty.buffer.ByteBuf;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.handler.codec.MessageToByteEncoder;
  6. public class PersonEncoder extends MessageToByteEncoder<Person>  {
  7. @Override
  8. protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {
  9. out.writeBytes(ByteObjConverter.objectToByte(msg));
  10. }
  11. }
客户端2发送String格式的协议:Client2 StringEncoder 同样使用了客户端1中定义的ClientInitHandler 进行数据发送操作。
1、Client2 
  1. package com.guowl.testobjcoder.client2;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioSocketChannel;
  10. import com.guowl.testobjcoder.ClientInitHandler;
  11. import com.guowl.testobjcoder.Person;
  12. public class Client2 {
  13. public void connect(String host, int port) throws Exception {
  14. EventLoopGroup workerGroup = new NioEventLoopGroup();
  15. try {
  16. Bootstrap b = new Bootstrap();
  17. b.group(workerGroup);
  18. b.channel(NioSocketChannel.class);
  19. b.option(ChannelOption.SO_KEEPALIVE, true);
  20. b.handler(new ChannelInitializer<SocketChannel>() {
  21. @Override
  22. public void initChannel(SocketChannel ch) throws Exception {
  23. ch.pipeline().addLast(new StringEncoder());
  24. Person person = new Person();
  25. person.setName("guoxy");
  26. person.setSex("girl");
  27. person.setAge(4);
  28. ch.pipeline().addLast(new ClientInitHandler(person));
  29. }
  30. });
  31. ChannelFuture f = b.connect(host, port).sync();
  32. f.channel().closeFuture().sync();
  33. } finally {
  34. workerGroup.shutdownGracefully();
  35. }
  36. }
  37. public static void main(String[] args) throws Exception {
  38. Client2 client = new Client2();
  39. client.connect("127.0.0.1", 8000);
  40. }
  41. }
2、StringEncoder 把Person对象转换成固定格式的String的二进制流进行传送
  1. package com.guowl.testobjcoder.client2;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.channel.ChannelHandlerContext;
  4. import io.netty.handler.codec.MessageToByteEncoder;
  5. import com.guowl.testobjcoder.Person;
  6. public class StringEncoder extends MessageToByteEncoder<Person> {
  7. @Override
  8. protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {
  9. // 转成字符串:name:xx;age:xx;sex:xx;
  10. StringBuffer sb = new StringBuffer();
  11. sb.append("name:").append(msg.getName()).append(";");
  12. sb.append("age:").append(msg.getAge()).append(";");
  13. sb.append("sex:").append(msg.getSex()).append(";");
  14. out.writeBytes(sb.toString().getBytes());
  15. }
  16. }
其它:工具类ByteBufToBytes(读取ByteBuf数据的工具类)、ByteObjConverter(Object与byte互转的工具类)在以前的文章中已经存在,在此省略。
 
注意事项:
1、该段代码能运行出结果,但是运行的时候会报 io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1 异常,已经解决。日志中的提示信息为:
An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception
说明缺少exceptionCaught方法,在server端最后一个Handler中增加这个方法即可。
2、PersonDecoder和StringDecoder中有一个if判断,是为了判断消息究竟是什么协议。如果是String协议的话,格式是【name:xx;age:xx;sex:xx;】,第一个字母是英文字母n,所以判断协议类型时候是读取二进制流的第一个字符进行判断,当然这种判断方式非常幼稚,以后有机会可以进行改善。

netty 支持多种通讯协议的更多相关文章

  1. MySQL 通讯协议

    Client/Server 通讯协议用于客户端链接.代理.主备复制等,支持 SSL.压缩,在链接阶段进行认证,在执行命令时可以支持 Prepared Statements 以及 Stored Proc ...

  2. ActiveMQ学习笔记(7)----ActiveMQ支持的传输协议

    1. 连接到ActiveMQ Connector: Active提供的,用来实现连接通讯的功能,包括:client-to-broker,broker-to-broker.ActiveMQ允许客户端使用 ...

  3. Netty 对通讯协议结构设计的启发和总结

    Netty 通讯协议结构设计的总结 key words: 通信,协议,结构设计,netty,解码器,LengthFieldBasedFrameDecoder 原创 包含与机器/设备的通讯协议结构的设计 ...

  4. netty 自定义通讯协议

    Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象.基于 ...

  5. RPC基于http协议通过netty支持文件上传下载

    本人在中间件研发组(主要开发RPC),近期遇到一个需求:RPC基于http协议通过netty支持文件上传下载 经过一系列的资料查找学习,终于实现了该功能 通过netty实现文件上传下载,主要在编解码时 ...

  6. HslCommunication库的二次协议扩展,适配第三方通讯协议开发,基础框架支持长短连接模式

    本文将使用一个gitHub开源的项目来扩展实现二次协议的开发,该项目已经搭建好了基础层架构,并实现了三菱,西门子,欧姆龙,MODBUS-TCP的通讯示例,也可以参照这些示例开发其他的通讯协议,并Pul ...

  7. 基于dubbo框架下的RPC通讯协议性能测试

    一.前言 Dubbo RPC服务框架支持丰富的传输协议.序列化方式等通讯相关的配置和扩展.dubbo执行一次RPC请求的过程大致如下:消费者(Consumer)向注册中心(Registry)执行RPC ...

  8. Mavlink - 无人机通讯协议

    http://qgroundcontrol.org/mavlink/start mavlink协议介绍https://pixhawk.ethz.ch/mavlink/ 消息简介 MAVLink简介 M ...

  9. Modbus通讯协议

    <ignore_js_op> O1CN01P1wxTI1dCdw5nAeMO_!!85243700.jpg (287.43 KB, 下载次数: 0) 下载附件  保存到相册 2019-6- ...

随机推荐

  1. 虚拟机 VMware安装系统,提示此主机支持Intel VT-x,但Intel VT-x处于禁用状态

    VMware提示此主机支持Intel VT-x,但Intel VT-x处于禁用状态 VMware提示此主机支持Intel VT-x,但Intel VT-x处于禁用状态这是怎么回事呢? Intel VT ...

  2. 关于NOIP2018初赛

    题面 这次PJ初赛有点傻了,可能是因为兴华水土不服吧(在这荒度了六年级的光阴). 选择题 DDDBBAAAABABBBB 第四题 当时懵了,我啥也不知道,于是就开始蒙 A.LAN B.WAN C.MA ...

  3. 历数依赖注入的N种玩法

    历数依赖注入的N种玩法 在对ASP.NET Core管道中关于依赖注入的两个核心对象(ServiceCollection和ServiceProvider)有了足够的认识之后,我们将关注的目光转移到编程 ...

  4. 002 jquery基本选择器

    1.选择器 2.基本选择器 3.程序(包含以上五种基本选择器) <!DOCTYPE html> <html> <head> <meta charset=&qu ...

  5. Python3.7.2,在Linux上跑来跑去的,是在升级打怪么?

    Python3.7.2,在Linux上跑来跑去的,是在升级打怪么?   前不久,发布了Python在Windows(程序员:Python学不学?完全没必要纠结)和Mac OS(我是Python,P派第 ...

  6. SpringMVC框架02——SpringMVC的Controller详解

    1.基于注解的控制器 1.1.@Controller 注解类型 在SpringMVC中使用org.springframework.stereotype.Controller注解类型声明某类的实例是一个 ...

  7. 在 Intellij IDEA 中部署 Java 应用到 阿里云 ECS

    你有没有怀疑过人生 多的去了 在开发过程中,发布部署项目是一件令人头疼的事 拿springboot项目来说吧(springboot算是已经极大简化了部署了) 步骤 运行clean install 将打 ...

  8. 美团开源Graver框架:用“雕刻”诠释iOS端UI界面的高效渲染

    Graver 是一款高效的 UI 渲染框架,它以更低的资源消耗来构建十分流畅的 UI 界面.Graver 独创性的采用了基于绘制的视觉元素分解方式来构建界面,得益于此,该框架能让 UI 渲染过程变得更 ...

  9. 算法初级面试题01——认识时间复杂度、对数器、 master公式计算时间复杂度、小和问题和逆序对问题

    虽然以前学过,再次回顾还是有别样的收获~ 认识时间复杂度 常数时间的操作:一个操作如果和数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作. 时间复杂度为一个算法流程中,常数操作数量的指标.常 ...

  10. JDBC之 连接池

    JDBC之 连接池 有这样的一种现象: 用java代码操作数据库,需要数据库连接对象,一个用户至少要用到一个连接.现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力 ...