通讯协议,指的是把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. JavaEE之JavaWeb简介

  2. DNS的服务器和客户端的配置

    内网环境Linux发行版本均采用centos为主,centos下DNS服务端的搭建步骤如下: DNS master节点搭建步骤: 安装组件: yum install bind;      yum in ...

  3. BI生态圈常用端口使用配置总结

    Hadoop集群的各部分一般都会使用到多个端口,有些是daemon之间进行交互之用,有些是用于RPC访问以及HTTP访问.而随着Hadoop周边组件的增多,完全记不住哪个端口对应哪个应用,特收集记录如 ...

  4. CAS5.2x单点登录(一)——搭建cas服务器

    系列文章列表: https://blog.csdn.net/u013825231/article/category/7517313 单点登录的介绍 单点登录(Single Sign On ,简称SSO ...

  5. ASP.Net1

    一.Web应用程序与传统桌面应用程序的不同: 1.产品级的Web应用程序总是包括至少两台联网的机器:一台承载网站,另一台在Web浏览器中查看数据. 即:我们通过自己的电脑浏览Web程序,这个程序会向服 ...

  6. HTML5练习3

    1.输入问题,判断答案,按钮倒计时 主要代码: <!doctype html> <html> <head> <meta charset="utf-8 ...

  7. 【PAT】1032 Sharing (25)(25 分)

    1032 Sharing (25)(25 分) To store English words, one method is to use linked lists and store a word l ...

  8. 记2013年度成都MVP社区巡讲

    上个周六在天府软件园A区的翼起来咖啡举行了MVP社区巡讲成都站的活动,这个巡讲活动去年也搞过一次. MVP社区巡讲是 @微软中国MVP项目组 支持的,由各地的MVP担任讲师,给本地技术社区提供的一种社 ...

  9. SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.

    SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after ...

  10. InnoDB的锁机制浅析(一)—基本概念/兼容矩阵

    InnoDB锁的基本概念 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Record锁/Gap锁/Next-key ...