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.pswp.cn/news/392903.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392903.shtml
英文地址,請注明出處:http://en.pswp.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;這主要…