通讯协议,指的是把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. 一次“ora-12170 tns 连接超时”的经历

      win7    64位系统 oracle  10g   64位 plsql之前连接是好使的,突然连接不上,提示错误“ora-12170 tns 连接超时” 1.ping IP    没有问题 2. ...

  2. python小记

    最近有匹骚猪用微信骚扰我,我很是气愤, 自学一波脚本: 学习目的:用脚本回击回去,通过py写一个脚本,一次性给别人发n条消息: mac上自学python: brew install python3(自 ...

  3. ICCV2013 录用论文(目标跟踪相关部分)(转)

    单目标(表观模型): 1. Seunghoon Hong, BohyungHan. Orderless Trackingthrough Model-Averaged Density Estimatio ...

  4. NFS基础配置

    需要安装的包: rpc-bind nfs-utils 修改配置文件 /etc/exports 配置 /tmp *(ro) 修改配置之后记得重启服务 sudo systemctl restart nfs ...

  5. AxureRP8实战手册

    基础操作篇 本篇包含56种常见的基础操作,初学者应在掌握本篇内容后再进行实战案例篇的学习,以免产生学习障碍.同时,建议具备一定基础的读者学习本篇中相对生疏的内容,并加以掌握. 第1章 使用元件 本文目 ...

  6. 1195: [HNOI2006]最短母串

    思路:好像以前谁问过我这题...  状个压就好啦, 把包含在其他串中的字符串删掉, 预处理除每两个字符串之间的关系, dp[ state ][ i ] 表示在state的状态下, 最后一个字符串是第i ...

  7. 036 SQLContext和HiveContext

    1.SqlContext SQLContext依赖SparkContext 功能:支持SparkSQL操作(不依赖Hive) SQLContext在一个JVM中默认允许存在多个 只有SQLContex ...

  8. QT5 样式随笔

    Qt的窗口背景及窗口风格统一与焕肤 button = new QPushButton(this);button->setStyleSheet("QPushButton{color:re ...

  9. http://blog.mn886.net/jqGrid/

    http://blog.mn886.net/jqGrid/  中文版 http://www.trirand.com/blog/jqgrid/jqgrid.html  英文版

  10. 括弧匹配检验(check.cpp)

    [问题描述]        假设表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,如([ ]())或[([ ][ ])]等为正确的匹配,[( ])或([ ]( )或 ( ( ) ) )均为错 ...