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. POI操作Excel详解,HSSF和XSSF两种方式

    package com.tools.poi.lesson1; import java.io.FileInputStream; import java.io.FileNotFoundException; ...

  2. 关于tp5 的验证码遇到的一些问题

    问题1: 网上大部分给的安装包是: composer require topthink/think-captcha 但是会提示你下载失败说要你回复*****的原始数据啥的 那可能是因为你的安装环境版本 ...

  3. Java基础90 MySQL触发器

    1.创建触发器 CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt ...

  4. 解决MySQL忘记root密码

    网上有很多关于忘记MySQL root密码的一些文章,里面都有写怎么去解决,但有时觉得写得太恶心,要么一字不漏的抄别人的,要么就说得不清不楚,好了,不吐槽了,以下是解决的整个过程. 首先我们要知道忘记 ...

  5. Nginx安装及常用命令

    一.选定源码目录 cd /usr/local/src 可以是任何目录,本文选定的是/usr/local/src 二.安装依赖库 yum install gcc yum install pcre-dev ...

  6. LOJ 10127 -「一本通 4.3 练习 1」最大数

    题面 题目描述 给定一个正整数数列 $a_1, a_2, a_3, \dots , a_n$,每一个数都在 0~p-1之间.可以对这列数进行两种操作: 添加操作:向序列后添加一个数,序列长度变成 n+ ...

  7. 6. 缓存 - 《APS.NET本质论》

    CaChe是ASP.NET中唯一可以根据服务器使用情况,动态管理内存使用的状态管理方案.我们通过每个缓存数据的键值字符串来区分缓存的数据. 简单案例来说.将数据从数据库/文件取出放在服务器内存中,后来 ...

  8. php生成随机数

    生成1-10之间的随机数,不重复. 方法一:用shuffle函数. <?php $arr=range(1,10); shuffle($arr); foreach($arr as $values) ...

  9. API的防重

    说说API的防重放机制 2017-03-20 18:19 by 轩脉刃, 685 阅读, 7 评论, 收藏, 编辑 说说API的防重放机制 我们在设计接口的时候,最怕一个接口被用户截取用于重放攻击.重 ...

  10. P1855 榨取kkksc03 二维费用背包

    Kkksc03的时间和金钱是有限的,所以他很难满足所有同学的愿望.所以他想知道在自己的能力范围内,最多可以完成多少同学的愿望? 输入输出格式 输入格式: 第一行,n M T,表示一共有n(n<= ...