Mina、Netty、Twisted一起学(五):整合protobuf

protobuf是谷歌的Protocol Buffers的简称,用于结构化数据和字节码之间互相转换(序列化、反序列化),一般应用于网络传输,可支持多种编程语言。

protobuf怎样使用这里不再介绍,本文主要介绍在MINA、Netty、Twisted中怎样使用protobuf,不了解protobuf的同学能够去參考我的还有一篇博文。


在前面的一篇博文中。有介绍到一种用一个固定为4字节的前缀Header来指定Body的字节数的一种消息切割方式。在这里相同要使用到。

仅仅是当中Body的内容不再是字符串,而是protobuf字节码。


在处理业务逻辑时,肯定不希望还要对数据进行序列化和反序列化。而是希望直接操作一个对象,那么就须要有对应的编码器和解码器。将序列化和反序列化的逻辑写在编码器和解码器中。有关编码器和解码器的实现,上一篇博文中有介绍。

Netty包中已经自带针对protobuf的编码器和解码器。那么就不用再自己去实现了。而MINA、Twisted还须要自己去实现protobuf的编码器和解码器。

这里定义一个protobuf数据结构,用于描写叙述一个学生的信息。保存为StudentMsg.proto文件:

message Student {// IDrequired int32 id = 1;  // 姓名required string name = 2;// emailoptional string email = 3;// 朋友repeated string friends = 4;
}

用StudentMsg.proto分别生成Java和Python代码。将代码加入到对应的项目中。

生成的代码就不再贴上来了。

以下分别介绍在Netty、MINA、Twisted怎样使用protobuf来传输Student信息。

Netty:

Netty自带protobuf的编码器和解码器,各自是ProtobufEncoder和ProtobufDecoder。须要注意的是,ProtobufEncoder和ProtobufDecoder仅仅负责protobuf的序列化和反序列化,而处理消息Header前缀和消息切割的还须要LengthFieldBasedFrameDecoder和LengthFieldPrepender。LengthFieldBasedFrameDecoder即用于解析消息Header前缀。依据Header中指定的Body字节数截取Body,LengthFieldPrepender用于在wirte消息时在消息前面加入一个Header前缀来指定Body字节数。


public class TcpServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch)throws Exception {ChannelPipeline pipeline = ch.pipeline();// 负责通过4字节Header指定的Body长度将消息切割pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));// 负责将frameDecoder处理后的完整的一条消息的protobuf字节码转成Student对象pipeline.addLast("protobufDecoder",new ProtobufDecoder(StudentMsg.Student.getDefaultInstance()));// 负责将写入的字节码加上4字节Header前缀来指定Body长度pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));// 负责将Student对象转成protobuf字节码pipeline.addLast("protobufEncoder", new ProtobufEncoder());pipeline.addLast(new TcpServerHandler());}});ChannelFuture f = b.bind(8080).sync();f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}
}

处理事件时,接收和发送的參数直接就是Student对象:

public class TcpServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 读取client传过来的Student对象StudentMsg.Student student = (StudentMsg.Student) msg;System.out.println("ID:" + student.getId());System.out.println("Name:" + student.getName());System.out.println("Email:" + student.getEmail());System.out.println("Friends:");List<String> friends = student.getFriendsList();for(String friend : friends) {System.out.println(friend);}// 新建一个Student对象传到clientStudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();builder.setId(9);builder.setName("server");builder.setEmail("123@abc.com");builder.addFriends("X");builder.addFriends("Y");StudentMsg.Student student2 = builder.build();ctx.writeAndFlush(student2);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}

MINA:

在MINA中没有针对protobuf的编码器和解码器。可是能够自己实现一个功能和Netty一样的编码器和解码器。


编码器:

public class MinaProtobufEncoder extends ProtocolEncoderAdapter {@Overridepublic void encode(IoSession session, Object message,ProtocolEncoderOutput out) throws Exception {StudentMsg.Student student = (StudentMsg.Student) message;byte[] bytes = student.toByteArray(); // Student对象转为protobuf字节码int length = bytes.length;IoBuffer buffer = IoBuffer.allocate(length + 4);buffer.putInt(length); // write headerbuffer.put(bytes); // write bodybuffer.flip();out.write(buffer);}
}

解码器:

public class MinaProtobufDecoder extends CumulativeProtocolDecoder {@Overrideprotected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {// 假设没有接收完Header部分(4字节)。直接返回falseif (in.remaining() < 4) {return false;} else {// 标记開始位置,假设一条消息没传输完毕则返回到这个位置in.mark();// 读取header部分,获取body长度int bodyLength = in.getInt();// 假设body没有接收完整,直接返回falseif (in.remaining() < bodyLength) {in.reset(); // IoBuffer position回到原来标记的地方return false;} else {byte[] bodyBytes = new byte[bodyLength];in.get(bodyBytes); // 读取body部分StudentMsg.Student student = StudentMsg.Student.parseFrom(bodyBytes); // 将body中protobuf字节码转成Student对象out.write(student); // 解析出一条消息return true;}}}
}

MINAserver加入protobuf的编码器和解码器:

public class TcpServer {public static void main(String[] args) throws IOException {IoAcceptor acceptor = new NioSocketAcceptor();// 指定protobuf的编码器和解码器acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new MinaProtobufEncoder(), new MinaProtobufDecoder()));acceptor.setHandler(new TcpServerHandle());acceptor.bind(new InetSocketAddress(8080));}
}

这样。在处理业务逻辑时,就和Netty一样了:

public class TcpServerHandle extends IoHandlerAdapter {@Overridepublic void exceptionCaught(IoSession session, Throwable cause)throws Exception {cause.printStackTrace();}@Overridepublic void messageReceived(IoSession session, Object message)throws Exception {// 读取client传过来的Student对象StudentMsg.Student student = (StudentMsg.Student) message;System.out.println("ID:" + student.getId());System.out.println("Name:" + student.getName());System.out.println("Email:" + student.getEmail());System.out.println("Friends:");List<String> friends = student.getFriendsList();for(String friend : friends) {System.out.println(friend);}// 新建一个Student对象传到clientStudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();builder.setId(9);builder.setName("server");builder.setEmail("123@abc.com");builder.addFriends("X");builder.addFriends("Y");StudentMsg.Student student2 = builder.build();session.write(student2);}
}

Twisted:

在Twisted中。首先定义一个ProtobufProtocol类,继承Protocol类,充当编码器和解码器。处理业务逻辑的TcpServerHandle类再继承ProtobufProtocol类。调用或重写ProtobufProtocol提供的方法。

# -*- coding:utf-8 –*-from struct import pack, unpack
from twisted.internet.protocol import Factory
from twisted.internet.protocol import Protocol
from twisted.internet import reactor
import StudentMsg_pb2# protobuf编码、解码器
class ProtobufProtocol(Protocol):# 用于临时存放接收到的数据_buffer = b""def dataReceived(self, data):# 上次未处理的数据加上本次接收到的数据self._buffer = self._buffer + data# 一直循环直到新的消息没有接收完整while True:# 假设header接收完整if len(self._buffer) >= 4:# header部分,按大字节序转int,获取body长度length, = unpack(">I", self._buffer[0:4])# 假设body接收完整if len(self._buffer) >= 4 + length:# body部分,protobuf字节码packet = self._buffer[4:4 + length]# protobuf字节码转成Student对象student = StudentMsg_pb2.Student()student.ParseFromString(packet)# 调用protobufReceived传入Student对象self.protobufReceived(student)# 去掉_buffer中已经处理的消息部分self._buffer = self._buffer[4 + length:]else:break;else:break;def protobufReceived(self, student):raise NotImplementedErrordef sendProtobuf(self, student):# Student对象转为protobuf字节码data = student.SerializeToString()# 加入Header前缀指定protobuf字节码长度self.transport.write(pack(">I", len(data)) + data)# 逻辑代码
class TcpServerHandle(ProtobufProtocol):# 实现ProtobufProtocol提供的protobufReceiveddef protobufReceived(self, student):# 将接收到的Student输出print 'ID:' + str(student.id)print 'Name:' + student.nameprint 'Email:' + student.emailprint 'Friends:'for friend in student.friends:print friend# 创建一个Student并发送给clientstudent2 = StudentMsg_pb2.Student()student2.id = 9student2.name = 'server'.decode('UTF-8') # 中文须要转成UTF-8字符串student2.email = '123@abc.com'student2.friends.append('X')student2.friends.append('Y')self.sendProtobuf(student2)factory = Factory()
factory.protocol = TcpServerHandle
reactor.listenTCP(8080, factory)
reactor.run()


以下是Java编写的一个client測试程序:

public class TcpClient {public static void main(String[] args) throws IOException {Socket socket = null;DataOutputStream out = null;DataInputStream in = null;try {socket = new Socket("localhost", 8080);out = new DataOutputStream(socket.getOutputStream());in = new DataInputStream(socket.getInputStream());// 创建一个Student传给serverStudentMsg.Student.Builder builder = StudentMsg.Student.newBuilder();builder.setId(1);builder.setName("client");builder.setEmail("xxg@163.com");builder.addFriends("A");builder.addFriends("B");StudentMsg.Student student = builder.build();byte[] outputBytes = student.toByteArray(); // Student转成字节码out.writeInt(outputBytes.length); // write headerout.write(outputBytes); // write bodyout.flush();// 获取server传过来的Studentint bodyLength = in.readInt();  // read headerbyte[] bodyBytes = new byte[bodyLength];in.readFully(bodyBytes);  // read bodyStudentMsg.Student student2 = StudentMsg.Student.parseFrom(bodyBytes); // body字节码解析成StudentSystem.out.println("Header:" + bodyLength);System.out.println("Body:");System.out.println("ID:" + student2.getId());System.out.println("Name:" + student2.getName());System.out.println("Email:" + student2.getEmail());System.out.println("Friends:");List<String> friends = student2.getFriendsList();for(String friend : friends) {System.out.println(friend);}} finally {// 关闭连接in.close();out.close();socket.close();}}
}

用client分别測试上面三个TCPserver:

server输出:

ID:1
Name:client
Email:xxg@163.com
Friends:
A
B

client输出:

Header:32
Body:
ID:9
Name:server
Email:123@abc.com
Friends:
X
Y


作者:叉叉哥   转载请注明出处:http://blog.csdn.net/xiao__gui/article/details/38864961

MINA、Netty、Twisted一起学系列

MINA、Netty、Twisted一起学(一):实现简单的TCPserver

MINA、Netty、Twisted一起学(二):TCP消息边界问题及按行切割消息

MINA、Netty、Twisted一起学(三):TCP消息固定大小的前缀(Header)

MINA、Netty、Twisted一起学(四):定制自己的协议

MINA、Netty、Twisted一起学(五):整合protobuf

MINA、Netty、Twisted一起学(六):session

MINA、Netty、Twisted一起学(七):公布/订阅(Publish/Subscribe)

MINA、Netty、Twisted一起学(八):HTTPserver

MINA、Netty、Twisted一起学(九):异步IO和回调函数

MINA、Netty、Twisted一起学(十):线程模型

MINA、Netty、Twisted一起学(十一):SSL/TLS

MINA、Netty、Twisted一起学(十二):HTTPS

源代码

https://github.com/wucao/mina-netty-twisted


转载于:https://www.cnblogs.com/wzzkaifa/p/6832163.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392903.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 1. 两数之和(map)

给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 示例: 给定 nums [2, 7, 11, 15], target …

Redis 3.0.1 安装和配置

一、下载&#xff0c;解压和编译Redis 12345# cd /tmp # wget http://download.redis.io/releases/redis-3.0.1.tar.gz # tar xzf redis-3.0.1.tar.gz # cd redis-3.0.1 # make二、下载、安装tclsh 测试编译&#xff1a; 1# make test得到如下错误信息&#xff1a; …

2021年南宁二中高考成绩查询,2021广西高考圆满结束,6月23日可查询成绩

6月8日下午&#xff0c;2021年高考统考圆满结束。今年广西参加高考统考考生人数40.05万余人&#xff0c;比2020年增加了2.2万人。我区预计6月23日可查询高考成绩&#xff0c;6月24日起可陆续填报志愿&#xff0c;我区的网上咨询会将于6月25日至27日举办。▲高考结束&#xff0c…

29 Python - 字符与编码

字符与编码 01 字符串本质 Python字符串相关概念 字符串 str 字节 bytes 字节数组 bytearray 电脑字符串存储机制 字符库&#xff1a;A、B每个字符有一个代码点如A是65 B为66&#xff0c;这种是方便人类读写的形式&#xff0c;但是最终需要存入计算机的CPU和内存&…

Linux 内存管理与系统架构设计

Linux 提供各种模式&#xff08;比如&#xff0c;消息队列&#xff09;&#xff0c;但是最著名的是 POSIX 共享内存&#xff08;shmem&#xff0c;shared memory&#xff09;。 Linux provides a variety of schemes (such as message queues), but most notable is POSIX shar…

如何正确使用Node.js中的事件

by Usama Ashraf通过Usama Ashraf 如何正确使用Node.js中的事件 (How to use events in Node.js the right way) Before event-driven programming became popular, the standard way to communicate between different parts of an application was pretty straightforward: …

你的成功有章可循

读书笔记 作者 海军 海天装饰董事长 自我修炼是基础。通过自我学习&#xff0c;在预定目标的指引下&#xff0c;将获取的知识转化为个人能力&#xff0c;形成自我规律&#xff0c;不断循环&#xff0c;实现成功。 寻找和掌握规律&#xff0c;并熟练运用于实践&#xff0c;是成功…

98k用计算机图片,98K (HandClap)_谱友园地_中国曲谱网

《98K》文本歌词98K之歌-HandClap-抖音 制谱&#xff1a;孙世彦这首《HandClap》是Fitz&TheTantrums乐队演唱的一首歌曲&#xff0c;同时也是绝地求生中嚣张BGM&#xff0c;是一首吃鸡战歌&#xff01;这首歌谱曲者和填词者都是三个人&#xff1a;JeremyRuzumna&#xff0c…

qt之旅-1纯手写Qt界面

通过手写qt代码来认识qt程序的构成&#xff0c;以及特性。设计一个查找对话框。以下是设计过程1 新建一个empty qt project2 配置pro文件HEADERS \Find.h QT widgetsSOURCES \Find.cpp \main.cpp3 编写对话框的类代码例如以下&#xff1a;//Find.h #ifndef FIND_H #define F…

【随笔】写在2014年的第一天

想想好像就在不久前还和大家异常兴奋地讨论着世界末日的事&#xff0c;结果一晃也是一年前的事了。大四这一年&#xff0c;或者说整个2013年都是场摇摆不定的戏剧&#xff0c;去过的地方比前三年加起来还多的多&#xff0c;有时候也会恍惚地不知道自己现在在哪。简单记几笔&…

设计冲刺下载_如何运行成功的设计冲刺

设计冲刺下载by George Krasadakis通过乔治克拉萨达基斯(George Krasadakis) Design Sprints can generate remarkable output for your company — such as a backlog of impactful ideas, functional prototypes, learning and key insights from customers along with real…

leetcode 18. 四数之和(双指针)

给定一个包含 n 个整数的数组 nums 和一个目标值 target&#xff0c;判断 nums 中是否存在四个元素 a&#xff0c;b&#xff0c;c 和 d &#xff0c;使得 a b c d 的值与 target 相等&#xff1f;找出所有满足条件且不重复的四元组。 注意&#xff1a; 答案中不可以包含重…

WPF:从WPF Diagram Designer Part 4学习分组、对齐、排序、序列化和常用功能

在前面三篇文章中我们介绍了如何给图形设计器增加移动、选择、改变大小及面板、缩略图、框线选择和工具箱和连接等功能&#xff0c;本篇是这个图形设计器系列的最后一篇&#xff0c;将和大家一起来学习一下如何给图形设计器增加分组、对齐、排序、序列化等功能。 WPF Diagram D…

win7如何看计算机用户名和密码怎么办,win7系统电脑查看共享文件夹时不显示用户名和密码输入窗口的解决方法...

win7系统使用久了&#xff0c;好多网友反馈说win7系统电脑查看共享文件夹时不显示用户名和密码输入窗口的问题&#xff0c;非常不方便。有什么办法可以永久解决win7系统电脑查看共享文件夹时不显示用户名和密码输入窗口的问题&#xff0c;面对win7系统电脑查看共享文件夹时不显…

ASP.NET Core跨域设置

项目中经常会遇到跨域问题&#xff0c;解决方法&#xff1a; 在appsettings.json 文件中添加json项 {"Logging": {"LogLevel": {"Default": "Warning"}},"AllowedHosts": "*","AppCores": "https…

微信客户端<->腾讯微信服务器<->开发者服务器

出自 http://blog.csdn.net/hanjingjava/article/details/41653113 首先&#xff0c;通过Token验证&#xff0c;将公众号接入开发者服务器&#xff0c;这样客户端发给公众号的信息会被转发给开发者服务器&#xff1b; 第二&#xff0c;组装微信特定消息格式&#xff0c;返回给用…

idea提高调试超时_如何提高您的调试技能

idea提高调试超时by Nick Karnik尼克卡尼克(Nick Karnik) 如何提高您的调试技能 (How to Improve Your Debugging Skills) All of us write code that breaks at some point. That is part of the development process. When you run into an error, you may feel that you do…

leetcode 834. 树中距离之和(dp)

给定一个无向、连通的树。树中有 N 个标记为 0...N-1 的节点以及 N-1 条边 。第 i 条边连接节点 edges[i][0] 和 edges[i][1] 。返回一个表示节点 i 与其他所有节点距离之和的列表 ans。示例 1:输入: N 6, edges [[0,1],[0,2],[2,3],[2,4],[2,5]] 输出: [8,12,6,10,10,10] 解…

CSS设计指南(读书笔记 - 背景)

本文转自william_xu 51CTO博客&#xff0c;原文链接&#xff1a;http://blog.51cto.com/williamx/1140006&#xff0c;如需转载请自行联系原作者

在计算机网络中 带宽是什么,在计算机网络中,“带宽”用____表示。

答案查看答案解析:【解析题】计算机的发展经历了4个时代&#xff0c;各个时代划分的原则是根据()。【解析题】计算机网络的最主要的功能是______。【解析题】冯.诺依曼提出的计算机工作原理为____。【解析题】计算机的通用性使其可以求解不同的算术和逻辑问题&#xff0c;这主要…