1. 程式人生 > >Protobuf簡單使用及其抓包分析

Protobuf簡單使用及其抓包分析

早之前就用過Google的Protobuf做資料編碼,一直沒有深入理解其中的原理,最近做了一次通訊抓包,發現其中很多Protobuf編碼的資料包,於是決定分析一下其中的資料包及其編碼。

一、Protobuf的使用

首先來簡單介紹一下Protobuf的使用,這裡以windows下java開發為例,幾個步驟:編寫*.proto ->使用google提供的protoc.exe生成*.java->專案中匯入protobuf的.jar包進行開發即可。先看這裡的*.proto檔案:

package com;

message CMsg
{
    required string msghead = 1;
    required string msgbody = 2;
}

message CMsgHead
{
    required int32 msglen = 1;
    required int32 msgtype = 2;
    required int32 msgseq = 3;
    required int32 termversion = 4;
    required int32 msgres = 5;
    required string termid = 6;
}

message CMsgReg
{
    optional int32 area = 1;
    optional int32 region = 2;
    optional int32 shop = 3;
    optional int32 ret = 4;
    optional string termid = 5;
}

使用protoc.exe生成java檔案,命令如下:

將生成的Msg.java及protobuf-java-2.3.0.jar匯入專案中進行開發,這裡寫一個伺服器端ProtobufServer及客戶端ProtobufClient

package com;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

import com.Msg.CMsg;
import com.Msg.CMsgHead;
import com.Msg.CMsgReg;

public class ProtoServer implements Runnable {

	@Override
	public void run() {
		try {
			System.out.println("beign:");
			ServerSocket serverSocket = new ServerSocket(12345);
			while (true) {
				System.out.println("等待接收使用者連線:");
				// 接受客戶端請求
				Socket client = serverSocket.accept();

				DataOutputStream dataOutputStream;
				DataInputStream dataInputStream;

				try {
					InputStream inputstream = client.getInputStream();
					dataOutputStream = new DataOutputStream(
							client.getOutputStream());

					byte len[] = new byte[1024];
					int count = inputstream.read(len);
					byte[] temp = new byte[count];
					for (int i = 0; i < count; i++) {
						temp[i] = len[i];
					}

					CMsg msg = CMsg.parseFrom(temp);

					CMsgHead head = CMsgHead.parseFrom(msg.getMsghead()
							.getBytes());
					System.out.println("==len===" + head.getMsglen());
					System.out.println("==res===" + head.getMsgres());
					System.out.println("==seq===" + head.getMsgseq());
					System.out.println("==type===" + head.getMsgtype());
					System.out.println("==Termid===" + head.getTermid());
					System.out.println("==Termversion==="
							+ head.getTermversion());

					CMsgReg body = CMsgReg.parseFrom(msg.getMsgbody()
							.getBytes());
					System.out.println("==area==" + body.getArea());
					System.out.println("==Region==" + body.getRegion());
					System.out.println("==shop==" + body.getShop());

					sendProtoBufBack(dataOutputStream);
					inputstream.close();

				} catch (Exception ex) {
					System.out.println(ex.getMessage());
					ex.printStackTrace();
				} finally {
					client.close();
					System.out.println("close");
				}
			}
			
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}

	private byte[] getProtoBufBack() {

		// head
		CMsgHead head = CMsgHead.newBuilder().setMsglen(10).setMsgtype(21)
				.setMsgseq(32).setTermversion(43).setMsgres(54)
				.setTermid("Server:head").build();

		// body
		CMsgReg body = CMsgReg.newBuilder().setArea(11).setRegion(22)
				.setShop(33).setRet(44).setTermid("Server:body").build();

		// Msg
		CMsg msg = CMsg.newBuilder()
				.setMsghead(head.toByteString().toStringUtf8())
				.setMsgbody(body.toByteString().toStringUtf8()).build();

		return msg.toByteArray();
	}

	private void sendProtoBufBack(DataOutputStream dataOutputStream) {

		byte[] backBytes = getProtoBufBack();
		
		// Integer len2 = backBytes.length;
		
		// byte[] cmdHead2 = BytesUtil.IntToBytes4(len2);

		try {
			// dataOutputStream.write(cmdHead2, 0, cmdHead2.length);
			dataOutputStream.write(backBytes, 0, backBytes.length);
			dataOutputStream.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		Thread desktopServerThread = new Thread(new ProtoServer());
		desktopServerThread.start();
	}

}

package com;

import java.io.InputStream;
import java.net.Socket;

import com.Msg.CMsg;
import com.Msg.CMsgHead;
import com.Msg.CMsgReg;

public class ProtoClient {

	public static void main(String[] args) {
		ProtoClient pc=new ProtoClient();
		System.out.println("beign:");
		pc.runget();
	}

	public void runget() {
		Socket socket = null;
		try {
			//socket = new Socket("localhost", 12345);
			socket = new Socket("192.168.85.152", 12345);
			// head
			CMsgHead head = CMsgHead.newBuilder().setMsglen(5).setMsgtype(1)
					.setMsgseq(3).setTermversion(41).setMsgres(5)
					.setTermid("Client:head").build();

			// body
			CMsgReg body = CMsgReg.newBuilder().setArea(11).setRegion(22)
					.setShop(33).setRet(44).setTermid("Clent:body").build();
			
			// Msg
			CMsg msg = CMsg.newBuilder()
					.setMsghead(head.toByteString().toStringUtf8())
					.setMsgbody(body.toByteString().toStringUtf8()).build();

			// 向伺服器傳送資訊
			System.out.println("sendMsg...");
			msg.writeTo(socket.getOutputStream());

			// 接受伺服器的資訊
			InputStream input = socket.getInputStream();

			System.out.println("recvMsg:");
			byte[] by = recvMsg(input);
			printMsg(CMsg.parseFrom(by));

			input.close();
			socket.close();
		} catch (Exception e) {
			System.out.println(e.toString());
		}
	}

	public void printMsg(CMsg g) {

		try {
			CMsgHead h = CMsgHead.parseFrom(g.getMsghead().getBytes());
			StringBuffer sb = new StringBuffer();
			if (h.hasMsglen())
				sb.append("==msglen===" + h.getMsglen() + "\n");
			if (h.hasMsgres())
				sb.append("==msgres===" + h.getMsgres() + "\n");
			if (h.hasMsgseq())
				sb.append("==msgseq===" + h.getMsgseq() + "\n");
			if (h.hasMsgtype())
				sb.append("==msgtype===" + h.getMsgtype() + "\n");
			if (h.hasTermid())
				sb.append("==termid===" + h.getTermid() + "\n");
			if (h.hasTermversion())
				sb.append("==termversion===" + h.getTermversion() + "\n");

			CMsgReg bo = CMsgReg.parseFrom(g.getMsgbody().getBytes());
			if (bo.hasArea())
				sb.append("==area==" + bo.getArea() + "\n");
			if (bo.hasRegion())
				sb.append("==region==" + bo.getRegion() + "\n");
			if (bo.hasShop())
				sb.append("==shop==" + bo.getShop() + "\n");
			if (bo.hasRet())
				sb.append("==ret==" + bo.getRet() + "\n");
			if (bo.hasTermid())
				sb.append("==termid==" + bo.getTermid() + "\n");

			System.out.println(sb.toString());

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public byte[] recvMsg(InputStream inpustream) {
		byte[] temp = null;
		try {

			byte len[] = new byte[1024];
			int count = inpustream.read(len);

			temp = new byte[count];
			for (int i = 0; i < count; i++) {
				temp[i] = len[i];
			}
			return temp;
		} catch (Exception e) {
			System.out.println(e.toString());
			return temp;
		}
	}
}

執行結果:

二、抓包分析

在上面socket通訊過程中我使用了wireshark對其進行抓包,結果分析如下圖

由上圖我們可以很清楚的看到,protobuf編碼其實類似tlv(tag length value)編碼,其內部就是(tag, length, value)的組合,其中tag由(field_number<<3)|wire_type計算得出,field_number由我們在proto檔案中定義,wire_type由protobuf根據proto中定義的欄位型別決定,length長度採用一種叫做Varint 的數字表示方法,它是一種緊湊的表示數字的方法,用一個或多個位元組來表示一個數字,值越小的數字使用越少的位元組數,具體細節可以谷歌Varint。總之Protobuf 序列化後所生成的二進位制訊息非常緊湊,這得益於 Protobuf 採用了上面的 Encoding 方法。

相關推薦

Protobuf簡單使用及其分析

早之前就用過Google的Protobuf做資料編碼,一直沒有深入理解其中的原理,最近做了一次通訊抓包,發現其中很多Protobuf編碼的資料包,於是決定分析一下其中的資料包及其編碼。一、Protobuf的使用首先來簡單介紹一下Protobuf的使用,這裡以windows下j

Fiddler是最強大最好用的Web調試工具之一--網站分析

als passport 運行機制 重新 腳本 win 默認 img 博客 Fiddler 教程 Fiddler是最強大最好用的Web調試工具之一,它能記錄所有客戶端和服務器的http和https請求,允許你監視,設置斷點,甚至修改輸入輸出數據. 使用Fiddler無

qxdm,qpst,qcat分析VoLTE SIP協商

可能 png mage mes 3.1 sage 需要 開始 不同 QXDM,QPST和QCAT是Qualcomm高通公司針對高通芯片的抓包分析工具。 QXDM抓包分析,QPST與手機com口連接,QCAT用來分析抓包產生的isf文件(log)。 使用版本: QXDM-3.

hdlc分析

hdlcR2(config)#int s2/2 R2(config-if)#ip addr 202.100.23.2 255.255.255.0 R2(config-if)#no shut R2(config-if)# *Aug 21 16:19:30.153: %LINK-3-UPDOWN: Interfa

PPP 分析

pppR2#conf t Enter configuration commands, one per line. End with CNTL/Z. R2(config)#default int s2/2 Interface Serial2/2 set to default configuration R2(

vlan 分析

vlan# vlan (virtual LAN ) # 一臺未設置任何VLAN的二層交換機上,任何廣播幀都會被轉發給除接收端口外的所有其他端口(Flooding)。VLAN通過限制廣播幀轉發的範圍分割了廣播域 # 802.1Q也即“Virtual Bridged Local Area Networks”(虛擬

DTP 分析

dtp# DTP(DynamicTrunking protocol)是思科私有協議為Trunk服務,前身是DISL。可以讓交換機間的鏈路自動協商是否形成Trunk。 # TRUNK介紹過配置Trunk用switchporttrunk encapsulation dot1Q和switchportmode tru

VTP 分析

vtp# 1、VTP協議介紹 # Cisco交換機一旦通過某種方式激活了幹線(Trunk),這些交換機會通過通告報文來指示哪些VLAN是可用的,並且會維持這些VLAN的相關信息,這種功能就叫做VTP(Vlan Trunking Protocol,VLAN中繼協議)。VTP是思科私有的協議,但大多數交換機都支持

STP 分析

stp# STP(Spanning-Tree Protocol,生成樹協議)的工作原理,在分層網絡中存在冗余鏈路的情況下容易引起流量環路,使用STP能夠動態的管理這些冗余鏈路;當某臺交換機的一條連接丟失時,另一條鏈路能迅速取代失敗鏈路,並且不會產生流量環路。文章主要包括下面幾點內容:冗余拓撲中存在的問題、生成

VLC 播放RTSP 分析

vlc 播放rtsp 抓包分析VLC 播放RTSP視頻抓包記錄:vlc -vvv rtsp://172.16.66.22/nuc.sdpOPTIONS: OPTIONS rtsp://172.16.66.22/nuc.sdp RTSP/1.0 CSeq: 2 User-Agent: LibVLC/2.2

TCP ------ 分析

com 分析 抓包 數據 技術 不用 一個 其他 -- 分析: ACK包可以和其他包合在一起 可以接收多個數據包後,一次性給一個應答,不用每個數據包一一對應給應答 TCP ------ 抓包分析

DHCP服務器的搭建及分析DHCP的實現

並不是 over bsp config 所有 沒有 實的 狀態 流動 原文:http://blog.51cto.com/liwenhui/105129 1、環境搭建: DC&DHCP SERVER IP:192.168.1.254 ( 這是一臺DC兼

分析工具—tcpdump

tcpdump tcpdump(dump the traffic on a network)是一款在Unix下一款比較實用的一款用於分析數據包的工具,它支持針對網絡層、協議、主機、網絡或端口的過濾,並提供and、or、not等邏輯語句、布爾表達式對報文的報頭匹配,在linux系統下如果未安裝可以通過yum

Fiddler分析

width attr ack -a inspect 訪問量 參考 註意 med 在Fiddler的web session界面捕獲到的HTTP請求如下圖所示: 各字段的詳細說明已經解釋過,這裏不再說明。需要註意的是#號列中的圖標,每種圖標代表不同的相應類型,具體的類型包

銳捷CCNA系列(二) Wireshark分析Ping過程

銳捷 CCNA 銳捷實戰 銳捷CCNA 數通 實訓目的 初步了解Wireshark的使用。 能分析Ping過程。 實訓背景 PING(Packet Internet Groper, 因特網包探索器),用於測試網絡是否連通的程序,在Windows、Linux、Unix下都是標配程序,Pi

分析】Charles和 夜神模擬器 對安卓應用進行分析

技術分享 windows red 工具 com nsh pro 4.2 name 準備工具 : 1 Charles : https://www.charlesproxy.com (收費) 2 夜神模擬器 : https://www.yeshen.com (免費)

Fidder詳解-取HTTPS清求(Web/App)分析(靠譜篇)

可能 clas 請求 設置代理 cer port 關閉 lan str 為什麽要學Fidder抓包? 學習接口,必須要學http協議,不要求您對協議的掌握有多深。只是希望你能夠了解什麽是協議、協議的報文、狀態碼等等!本文通過抓包工具Fidder帶你進入接口的大門。我們通過

從wireshark分析rtmp協議,並提取出H264視頻流

tmp mage idt 進制 tro shark src 技術 wid 利用wireshark抓取rtmp流數據, 分析到rtmp流後,寫入過濾條件,如 tcp.stream eq 6導出tcp流保存16進制的數據為純文本格式一定要選擇 Hex轉儲,然後點擊 “Sava

eNSP模擬器拓撲圖:浮動路由的實際作用和分析,默認路由的配置,分析

操作 劃線 with 提示 實驗 三臺 常用 了解 為什麽 本篇介紹一下浮動路由和默認路由的配置和一些講解本篇內容附帶詳細的分析講解。會敲命令並不代表什麽,能敲明白才是關鍵。 往後的篇章內容 都盡量不寫地址配置了,如果地址都還不會配置的話,可以去翻一翻其他博文了解一下 打開

韓劇TV UWP開發---分析

xsd 彈出 緩存 var name 技術 pre detail spl 一,使用工具 ①Fiddler 摘自百度百科Fiddler簡介: Fiddler是一個http協議調試代理工具,它能夠記錄並檢查所有你的電腦和互聯網之間的http通訊,設置斷點,查看所有的“進出