通讯协议,指的是把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. day17作业

    1.java.util  2.队列先进先出,栈堆先进后出   3.链表  4.LinkedList  5.TreeSet  6.Comparable  7.Map  8.next() 1.AC 2.A ...

  2. 有用的Python模块 - pprint

    当想在终端打印一个很大的字典或者一个很长的列表时,总是被print打印出来的效果气懵在电脑前,现在有pprint就不用担心啦. 最直接的使用方式就是 import pprint pprint.ppri ...

  3. linux上jdk管理

    查看CentOS自带JDK是否已安装. yum list installed |grep java. 若有自带安装的JDK,卸载CentOS系统自带Java环境? yum -y remove java ...

  4. navicate连接Linux下mysql慢,卡,以及mysql相关查询,授权

    方法,网上的办法是在my.ini的“[mysqld]”下面加入一行“skip-name-resolve”,就像这样: 然后保存并重启mysql服务即可. service mysqld restart ...

  5. ElastAlert告警

    ElastAlert告警 https://blog.csdn.net/qq_38369069/article/details/80842432

  6. 如何安装pycharm

    Ubuntu系统安装PyCharm教程(详细图文) 参考(http://jingyan.baidu.com/article/60ccbceb4e3b0e64cab19733.html)  

  7. WebSocket原理说明

    WebSocket原理说明 众所周知,Web应用的通信过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现.这种机制对于信息变化不是特别频繁的应 ...

  8. 全局查询文件linux

    在工作中,可能突然需要找到某个文件,这个又不知道,需要全局查询一下. 下面是命令行: find / -name "*.log" | xargs grep "elk&quo ...

  9. 032 Spark容错特性

    1.spark容错主要分为两个方面 其一是集群 再者为spark的应用程序. 2.Driver 重要补充: driver宕机: Spark On Yarn:总之,要重启 client:只能重启job ...

  10. 探究 encode 和 decode 的使用问题(Python)

    很多时候在写Python程序的时候都要在头部添加这样一行代码 #coding: utf-8 或者是这样 # -*- coding:utf-8 -*- 等等 这行代码的意思就是设定同一编码格式为utf- ...