CppGuide社区 CppGuide社区
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 最新谷歌C++风格指南(含C++17/20)
  • C++17详解
  • C++20完全指南
  • C++23快速入门
  • C++语言面试问题集锦
  • 🔥C/C++后端开发常见面试题解析 (opens new window)
  • 网络编程面试题 (opens new window)
  • 网络编程面试题 答案详解 (opens new window)
  • 聊聊WebServer作面试项目那些事儿 (opens new window)
  • 字节跳动面试官现身说 (opens new window)
  • 技术简历指南 (opens new window)
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 从零用C语言写一个Redis
  • Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go开发实用指南
    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
  • Rust编程

    • Rust编程指南
  • 数据库

    • SQL零基础指南
    • MySQL开发与调试指南
GitHub (opens new window)
  • 第1章高频C++11重难点知识解析

  • 第2章Linux GDB高级调试指南

  • 第3章C++多线程编程从入门到进阶

  • 第4章C++网络编程重难点解析

  • 第5章网络通信故障排查常用命令

  • 第6章高性能网络通信协议设计精要

    • 6.1 TCP 协议是流式协议
    • 6.2 如何解决粘包问题
    • 6.3 解包与处理
    • 6.4 从 struct 到 TLV——协议的演化历史
    • 6.5 整型数值的压缩
    • 6.6 通信协议设计时的注意事项
    • 6.7 包分片
    • 6.8 跨语言之间的网络通信协议识别与解析
    • 6.9 xml 与 json 格式协议
    • 6.10 一个自定义协议示例
    • 6.11 http 协议
    • 6.12 SMTP、POP3 协议与邮件客户端
    • 6.13 WebSocket 协议
  • 第7章高性能服务结构设计

  • 第8章Redis 网络通信模块源码分析

  • 第9章后端服务重要模块设计探索

  • C++后端开发进阶
  • 第6章高性能网络通信协议设计精要
zhangxf
2023-04-05

6.8 跨语言之间的网络通信协议识别与解析

在实际开发中,由于不同平台开发工具和编程语言的差别,我们需要不同编程语言之间去解析通信协议,这里以 C++ 和 Java 语言为例,通过一个示例来分析如何跨语言之间解析通信协议,编写跨平台代码。

这里以在 Java 中解析 C++ 网络数据包为例。 通常,这对于很多人来说是一件很困难的事情,所以只能变着法子使用第三方的库。其实只要你掌握了一定的基础知识,利用一些现成的字节流抓包工具(如 tcpdump、wireshark)很容易解决这个问题。我们这里使用 tcpdump 工具来尝试分析和解决这个问题。

首先,在我们需要明确字节序列的概念后,我们知道 x86 和 x64 系列的 CPU 使用小端编码,而数据在网络上传输,以及 Java 语言中,使用的是大端编码。 举个例子,看一个 x64 机器上的 32 位数值在内存中的存储方式,代码如下:

int main()
{
	int32_t i = 123456;
	
	return 0;
}
1
2
3
4
5
6

变量 i 在内存中的地址序列是 0x003CF7C4 ~ 0x003CF7C8,值为 40 e2 01 00。

十六进制 0001e240 值等于 10 进制 123456,对于整数 123456,十万位的数字 1 是权重最高的位,个位的数字 6 是权重最低的位,小端编码中权重高的位存储在内存地址高(内存地址值大)的位置,权重值低的位存储在内存地址低(内存地址值小)的位置,这就是所谓的"高高低低原则"(高位高地址,低位低地址)。 大端编码的规则与小端编码的规则相反,大端编码使用的是"高低低高原则",即权重高的位存储在内存地址值低的位置,权重低的位存储在内存地址高的位置。

如果我们一个 C++ 程序的 int32 值 123456 不作转换地传给 Java 程序,那么 Java 按照大端编码的形式读出来的值是:十六进制 40E20100 = 十进制 1088553216。所以,为了表达同样的值,要么在发送方将数据转换成网络字节序(big endian),要么让接收端做转换。

下面看一下如果 C++ 端传送一个类型为 msg 数据结构,Java 端该如何解析(由于 Java 中是没有指针的,也无法操作内存地址,导致很多开发者无从下手),下面利用 tcpdump 来寻找解决这个问题的思路。

#pragma pack(push, 1)
struct msg
{
	char 	compressflag;
	int32_t originsize;
	int32_t compresssize;
	char 	reservered[16];
	char 	buf[63];
}
#pragma pack(pop)
1
2
3
4
5
6
7
8
9
10

客户端发送的数据包:

利用 tcpdump 抓到的包如下:

放大一点:

我们白色标识出来就是我们收到的数据包。这里我想说明两点:

  • 如果我们知道发送端发送的字节流,再比对接收端收到的字节流,我们就能检测数据包的完整性,也可以利用这个来排查一些网络通信问题;
  • 对于 Java 程序只要按照这个顺序,先利用 java.net.Socket 的输出流 java.io.DataOutputStream 对象的 readByte、readInt32、readInt32、readBytes、readBytes 方法依次读出一个 char、int32、int32、16 个字节的字节数组、63 个字节数组即可。当然,为了还原像 int32 这样的整型值,我们需要额外做一些从 little-endian 向 big-endian 的转换工作。
上次更新: 2025/05/19, 16:52:22
6.7 包分片
6.9 xml 与 json 格式协议

← 6.7 包分片 6.9 xml 与 json 格式协议→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
第四章 Lambda函数
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式