Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象。基于这个思路,我自定义一种通讯协议:Server和客户端直接传输java对象。

实现的原理是通过Encoder把java对象转换成ByteBuf流进行传输,通过Decoder把ByteBuf转换成java对象进行处理,处理逻辑如下图所示:

传输的java bean为Person:

  1. package com.guowl.testobjcoder;
  2. import java.io.Serializable;
  3. // 必须实现Serializable接口
  4. public class Person implements Serializable{
  5. private static final long   serialVersionUID    = 1L;
  6. private String  name;
  7. private String  sex;
  8. private int     age;
  9. public String toString() {
  10. return "name:" + name + " sex:" + sex + " age:" + age;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public String getSex() {
  19. return sex;
  20. }
  21. public void setSex(String sex) {
  22. this.sex = sex;
  23. }
  24. public int getAge() {
  25. return age;
  26. }
  27. public void setAge(int age) {
  28. this.age = age;
  29. }
  30. }

Server端类:Server PersonDecoder 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. public class Server {
  11. public void start(int port) throws Exception {
  12. EventLoopGroup bossGroup = new NioEventLoopGroup();
  13. EventLoopGroup workerGroup = new NioEventLoopGroup();
  14. try {
  15. ServerBootstrap b = new ServerBootstrap();
  16. b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
  17. .childHandler(new ChannelInitializer<SocketChannel>() {
  18. @Override
  19. public void initChannel(SocketChannel ch) throws Exception {
  20. ch.pipeline().addLast(new PersonDecoder());
  21. ch.pipeline().addLast(new BusinessHandler());
  22. }
  23. }).option(ChannelOption.SO_BACKLOG, 128)
  24. .childOption(ChannelOption.SO_KEEPALIVE, true);
  25. ChannelFuture f = b.bind(port).sync();
  26. f.channel().closeFuture().sync();
  27. } finally {
  28. workerGroup.shutdownGracefully();
  29. bossGroup.shutdownGracefully();
  30. }
  31. }
  32. public static void main(String[] args) throws Exception {
  33. Server server = new Server();
  34. server.start(8000);
  35. }
  36. }

2、PersonDecoder:把ByteBuf流转换成Person对象,其中ByteBufToBytes是读取ButeBuf的工具类,上一篇文章中提到过,在此不在详述。ByteObjConverter是byte和obj的互相转换的工具。

  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. ByteBufToBytes read = new ByteBufToBytes();
  12. Object obj = ByteObjConverter.ByteToObject(read.read(in));
  13. out.add(obj);
  14. }
  15. }

3、BusinessHandler 读取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 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. }
  17. @Override
  18. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  19. }
  20. }

Client端的类:Client ClientInitHandler PersonEncoder

1、Client 建立与Server的连接

  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. ch.pipeline().addLast(new ClientInitHandler());
  23. }
  24. });
  25. ChannelFuture f = b.connect(host, port).sync();
  26. f.channel().closeFuture().sync();
  27. } finally {
  28. workerGroup.shutdownGracefully();
  29. }
  30. }
  31. public static void main(String[] args) throws Exception {
  32. Client client = new Client();
  33. client.connect("127.0.0.1", 8000);
  34. }
  35. }

2、ClientInitHandler 向Server发送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. @Override
  9. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  10. logger.info("HelloClientIntHandler.channelActive");
  11. Person person = new Person();
  12. person.setName("guowl");
  13. person.setSex("man");
  14. person.setAge(30);
  15. ctx.write(person);
  16. ctx.flush();
  17. }
  18. }

3、PersonEncoder 把Person对象转换成ByteBuf进行传送

  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. byte[] datas = ByteObjConverter.ObjectToByte(msg);
  10. out.writeBytes(datas);
  11. ctx.flush();
  12. }
  13. }

工具类:ByteObjConverter

  1. package com.guowl.utils;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. public class ByteObjConverter {
  8. public static Object ByteToObject(byte[] bytes) {
  9. Object obj = null;
  10. ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
  11. ObjectInputStream oi = null;
  12. try {
  13. oi = new ObjectInputStream(bi);
  14. obj = oi.readObject();
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. } finally {
  18. try {
  19. bi.close();
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. try {
  24. oi.close();
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. return obj;
  30. }
  31. public static byte[] ObjectToByte(Object obj) {
  32. byte[] bytes = null;
  33. ByteArrayOutputStream bo = new ByteArrayOutputStream();
  34. ObjectOutputStream oo = null;
  35. try {
  36. oo = new ObjectOutputStream(bo);
  37. oo.writeObject(obj);
  38. bytes = bo.toByteArray();
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. } finally {
  42. try {
  43. bo.close();
  44. } catch (IOException e) {
  45. e.printStackTrace();
  46. }
  47. try {
  48. oo.close();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. return (bytes);
  54. }
  55. }

通过上述代码,实现了Server端与Client端直接使用person对象进行通信的目的。基于此,可以构建更为复杂的场景:Server端同时支撑多种协议,不同的协议采用不同的Decoder进行解析,解析结果保持统一,这样业务处理类可以保持接口一致。下一节将编写这样一个案例。

本例中需要注意的事项是:

1、Person对象必须实现Serializable接口,否则不能进行序列化。

2、PersonDecoder读取ByteBuf数据的时候,并没有对多次流式数据进行处理,而是简单的一次性接收,如果数据量大的情况下,可能会出现数据不完整,这个问题会在后续的学习中解决。

netty 自定义通讯协议的更多相关文章

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

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

  2. netty 自定义协议

    netty 自定义协议 netty 是什么呢? 相信很多人都被人问过这个问题.如果快速准确的回复这个问题呢?网络编程框架,netty可以让你快速和简单的开发出一个高性能的网络应用.netty是一个网络 ...

  3. netty 支持多种通讯协议

    通讯协议,指的是把Netty通讯管道中的二进制流转换为对象.把对象转换成二进制流的过程.转换过程追根究底还是ChannelInboundHandler.ChannelOutboundHandler的实 ...

  4. 《精通并发与Netty》学习笔记(14 - 解决TCP粘包拆包(二)Netty自定义协议解决粘包拆包)

    一.Netty粘包和拆包解决方案 Netty提供了多个解码器,可以进行分包的操作,分别是: * LineBasedFrameDecoder (换行)   LineBasedFrameDecoder是回 ...

  5. 基于Netty的RPC架构学习笔记(九):自定义序列化协议

    文章目录 为什么需要自定义序列化协议

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

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

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

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

  8. websocket通讯协议(10版本)简介

    前言: 工作中用到了websocket 协议10版本的,英文的协议请看这里: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotoc ...

  9. 【转】西门子PLC以太网 通讯协议 解析

    一直想把三菱和西门子这两个使用频率最高的PLC上位通讯,融合到WCS系统的框架里: 现在三菱主流使用Q系列,使用的是MC协议, 前一段时间也写过一个入门介绍: 三菱Q系列通讯方式设计说明 去年8月份, ...

随机推荐

  1. LeetCode(19):删除链表的倒数第N个节点

    Medium! 题目描述: 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了 ...

  2. [转] HTML5中meta属性的使用详解

    meta属性在HTML中占据了很重要的位置.如:针对搜索引擎的SEO,文档的字符编码,设置刷新缓存等.虽然一些网页可能没有使用meta,但是作为正规军,我们还是有必要了解一些meta的属性,并且能够熟 ...

  3. 图学ES6-6.数值的扩展

  4. THUSC 2018 酱油记

    THUSC 2018 酱油记 游记分类:游记 Day \((-inf,-2]\) 自CTSC和APIO挂烂以后,仍然在停课集训,不过好像这两波考试让我的RP涨了一大波,因此模拟赛大多都考的不错,虽然经 ...

  5. 【LOJ】#2511. 「BJOI2018」双人猜数游戏

    题解 设\(f[p][a][b]\)表示询问了\(p\)次,答案是\(a,b\)是否会被猜出来 然后判断如果\(p = 1\) 第一个问的\(Alice\),那么\([s,\sqrt{nm}]\)约数 ...

  6. 记在VMware虚拟机中对网站进行性能压力测试的经历

    由于本次测试,仅仅是对静态网站首页进行的测试,所以没有涉及到MySQL数据库的性能监测 服务器基本配置 webbench测试工具 Linux上一款优秀的web性能压力测试工具.webbench最多可以 ...

  7. ubuntu18.04 安装mysql不出现设置 root 帐户的密码问题(装)

    ubuntu18.04 安装mysql不出现设置 root 帐户的密码问题      https://blog.csdn.net/NeptuneClouds/article/details/80995 ...

  8. Java对epub电子书类型切割

    Epub电子书切割 引言:由于公司存储电子书的格式是.epub.一本电子书加载的时候,如果电子书大的话,全部加载该电子书会非常的消耗时间和资源.非常的不合理.那么现在,将所有电子书按章切分.将拆分的电 ...

  9. Linux学习路线+资源

    Linux学习路线,个人收集分享 学习路线图 资源链接(蓝色下划线字体对应相应资源链接) Linux 基础 Linux 基础 Linux安装专题教程 Linux中文环境 Linux—从菜鸟到高手 鸟哥 ...

  10. 在ssh中利用Solr服务建立的界面化站内搜索

         继上次匆匆搭建起结合solr和nutch的所谓站内搜索引擎之后,虽当时心中兴奋不已,可是看了看百度,再只能看看我的控制台的打印出每个索引项的几行文字,哦,好像差距还是有点大……      简 ...