写在开篇·蓉儿又挖坑

上回说到,郭靖搞清楚了SOA是啥------不写死、动态找,让模块自己说"我会啥",让别人自己找"谁有我需要的"。
黄蓉咬了口糖葫芦:"行啊靖哥哥,SOA的门你算是摸到了。那你知道SOA是怎么'落地'的吗?"
郭靖憨憨摇头。
黄蓉在白板上写下几个大字:
SOME/IP
"SOME/IP的全称是Scalable service-Oriented MiddlewarE over IP。"
黄蓉掰着手指头翻译:
| 单词 | 含义 | 郭靖版理解 |
|---|---|---|
| Scalable | 可扩展的 | 车上的模块越来越多,这套协议要能撑得住 |
| service-Oriented | 面向服务的 | 为了SOA而生的 |
| MiddlewarE | 中间件 | 夹在应用和网络之间,帮你搞定通信的脏活累活 |
| over IP | 基于IP | 跑在以太网上 |
"翻译成人话:一套可扩展的、面向服务的、跑在IP上的中间件协议。"
郭靖挠头:"名字这么长,比降龙十八掌的招式还难记......"
"不用背全名,记住它的作用就行。"黄蓉竖起一根手指,"服务怎么发现、怎么调用、怎么回应,全是SOME/IP在干活。"
"那我该怎么学?"
黄蓉把一份文档扔给他:"先搞懂报文头长啥样。玄之又玄谓之道,报文头中藏玄妙。"
一、SOME/IP报文在完整数据帧中的位置
郭靖翻开文档,先看到一张"层级关系图":
第1层:以太网帧头(14字节)
| 字段 | 长度 | 说明 |
|---|---|---|
| 目的MAC | 6字节 | 接收方MAC地址 |
| 源MAC | 6字节 | 发送方MAC地址 |
| 以太网类型 | 2字节 | 0x0800 = IPv4 |
↓ 封装
第2层:IP头(20字节)
| 关键字段 | 说明 |
|---|---|
| 协议 | 0x06 = TCP,0x11 = UDP |
| 源IP | 发送方IP地址 |
| 目的IP | 接收方IP地址 |
↓ 封装
第3层:UDP/TCP头(8/20字节)
| 关键字段 | 说明 |
|---|---|
| 源端口 | 发送方端口 |
| 目的端口 | SOME/IP常用端口:30490 |
↓ 封装
第4层:SOME/IP报文头 + 载荷
← 今天要扒的"底裤"就在这里 →
郭靖恍然大悟:"哦~~SOME/IP不是直接裸跑在网线上的,它外面包着UDP/TCP、IP、MAC,一层套一层!"
黄蓉点头:"跟DoIP一样的套路。不同的是,DoIP走13400端口,SOME/IP通常走30490端口。"
二、SOME/IP报文头长啥样
郭靖翻到下一页,是一张完整的报文头结构表:
SOME/IP报文头结构(12字节)
| 字节范围 | 字段 | 长度 | 说明 |
|---|---|---|---|
| 0-1 | Service ID | 16位 | 哪个服务 |
| 2-3 | Method ID | 16位 | 哪个方法 |
| 4-7 | Length | 32位 | 从Request ID开始的长度 |
| 8-9 | Client ID | 16位 | 谁发的 |
| 10-11 | Session ID | 16位 | 第几次 |
| 12 | Protocol Version | 8位 | 协议版本(0x01) |
| 13 | Interface Version | 8位 | 接口版本 |
| 14 | Message Type | 8位 | 请求/响应/通知/只发不收 |
| 15 | Return Code | 8位 | 成功/失败 |
郭靖头大如斗:"这么多字段......比DoIP的8字节复杂多了!"
黄蓉白了他一眼:"DoIP是诊断,SOME/IP是通信。诊断是偶尔用,通信是天天用,头部自然要设计得更完善。就像你平时说话,比看病问诊花样多多了。"
三、逐个字段拆解(蓉儿版)
1. Message ID(32位)------服务的"身份证"
| 子字段 | 长度 | 说明 |
|---|---|---|
| Service ID | 16位 | 哪个服务?(放音乐?导航?) |
| Method ID | 16位 | 这个服务里的哪个方法?(播放?暂停?下一首?) |
黄蓉:"就像你去饭馆------Service ID是'川菜馆',Method ID是'点一份宫保鸡丁'。"
郭靖点头:"哦,一个管找地方,一个管点菜。"
2. Length(32位)------报文有多长
"从Request ID开始到报文结束的总长度。不包括前面的Message ID和Length自己。"
郭靖挠头:"为啥不包括?"
黄蓉:"约定俗成。就像你量身高,不量脚底下的垫子。"
3. Request ID(32位)------区分"谁问的、第几次问"
| 子字段 | 长度 | 说明 |
|---|---|---|
| Client ID | 16位 | 哪个客户端发的请求(座舱发的是0x01,音响发的是0x02) |
| Session ID | 16位 | 这个客户端的第几次请求(每次+1,用来匹配请求和响应) |
黄蓉:"座舱发请求,Client ID=0x01;音响发请求,Client ID=0x02。各管各的,不会串。"
"Session ID每次请求+1,用来匹配请求和响应。你问'现在几点了',我没听清,你再问一遍。Session ID不一样,我就知道是两次不同的请求。"
郭靖恍然大悟:"哦~~就像丐帮的竹棍,一根棍一个号,不会乱!"
4. Protocol Version(8位)------协议版本
目前是
0x01,以后升级了可能会变。
5. Interface Version(8位)------接口版本
同一个服务的接口可能会升级。比如放音乐服务V1只支持"播放/暂停",V2支持"播放/暂停/下一首/上一首"。
6. Message Type(8位)------这是什么类型的消息
| 类型 | 值 | 郭靖理解 |
|---|---|---|
| REQUEST | 0x00 | "你问他答" |
| RESPONSE | 0x80 | "有问才有答" |
| NOTIFICATION | 0x02 | "不等你问,我自己说" |
| FIRE&FORGET | 0x01 | "喊一嗓子,不管了" |
郭靖眼睛一亮:"NOTIFICATION不就是ECU主动报故障码?不用你问,它自己喊'我出毛病了'!"
7. Return Code(8位)------响应结果
| 返回值 | 值 | 郭靖理解 |
|---|---|---|
| E_OK | 0x00 | "成了" |
| E_NOT_OK | 0x01 | "没成" |
| E_UNKNOWN_SERVICE | 0x02 | "你说的服务我听不懂" |
| E_WRONG_INTERFACE_VERSION | 0x08 | "你用的版本太老了,我不认" |
四、郭靖复述(蓉儿抽查)
黄蓉把笔一扔:"行,你复述一遍。"
郭靖站起来,掰着手指头:
① Service ID + Method ID :哪个服务、哪个方法。
② Length :从Request ID开始有多长。
③ Client ID + Session ID :谁发的、第几次。
④ Protocol + Interface Version :协议版本、接口版本。
⑤ Message Type :是问还是答,还是自己报。
⑥ Return Code:成了还是没成。
黄蓉满意地点点头:"算你过关。"
五、SOME/IP vs DoIP 整体对比
| 对比项 | DoIP | SOME/IP |
|---|---|---|
| 端口 | 13400(UDP/TCP) | 30490(UDP/TCP,常用配置) |
| 报文头长度 | 8字节固定 | 12字节固定 |
| 核心字段 | Protocol Version、Payload Type、Payload Length | Service ID、Method ID、Length、Client ID、Session ID、Protocol Version、Interface Version、Message Type、Return Code |
| 通信模式 | 请求/响应(一问一答) | 请求/响应 + 事件通知 + 只发不收 |
| 郭靖理解 | 看病 | 聊天 |
郭靖感叹:"DoIP问一句答一句,SOME/IP既能问也能主动报。诊断是偶尔,聊天是常态。"
黄蓉咬了口糖葫芦:"总结到位。DoIP的底裤你扒过,SOME/IP的底裤今天也扒了一半。"
六、一个真实的SOME/IP报文(十六进制)
黄蓉从电脑里调出一个抓包文件:
00 01 00 01 00 00 00 10 12 34 00 01 01 01 02 00
先看它在整个数据帧里的位置:
| 层级 | 内容 | 说明 |
|---|---|---|
| 以太网帧头 | [目的MAC][源MAC][08 00] |
以太网类型=IPv4 |
| IP头 | [45 00 ... c0 a8 01 64 c0 a8 01 c8] |
源IP=192.168.1.100, 目的IP=192.168.1.200 |
| UDP头 | [源端口][目的端口=30490][长度][校验] |
SOME/IP端口 |
| SOME/IP报文头 | 00 01 00 01 00 00 00 10 12 34 00 01 01 01 02 00 |
16字节(头部12+载荷4) |
然后专门拆SOME/IP这部分:
| 字节 | 字段 | 值 | 说明 |
|---|---|---|---|
| 0-1 | Service ID | 0x0001 | 放音乐服务 |
| 2-3 | Method ID | 0x0001 | "播放"方法 |
| 4-7 | Length | 0x00000010 | 从Request ID开始共16字节 |
| 8-9 | Client ID | 0x1234 | 座舱发的 |
| 10-11 | Session ID | 0x0001 | 第一次请求 |
| 12 | Protocol Version | 0x01 | 版本1 |
| 13 | Interface Version | 0x01 | 接口版本1 |
| 14 | Message Type | 0x02 | NOTIFICATION(事件通知) |
| 15 | Return Code | 0x00 | 成功 |
郭靖挠头:"怎么是NOTIFICATION?不是请求?"
黄蓉:"这是音响主动上报的------'我的状态变了,正在播放'。这就是事件通知,不用你问,它自己说。"
七、黄蓉的小本本
郭靖趁黄蓉去倒茶,偷偷翻开她的笔记本。
某页写着:
玄之又玄谓之道,报文头中藏玄妙。
SOME/IP报文头12字节,比DoIP多了4字节。
外面包着UDP/IP/以太网,一层套一层。
DoIP问一句答一句,SOME/IP既能问也能主动报。
Service ID + Method ID = 唯一的"服务接口"。
Client ID + Session ID = 唯一不串的"会话"。
Message Type决定"说话方式"------问、答、主动报、喊一嗓子不管了。
旁边画了一个小人,笑眯眯地写着:
"靖哥哥总算摸着门边了。"
写在最后
这一篇最大的收获:
SOME/IP不是裸跑在网线上的,它外面裹着UDP/TCP头、IP头、以太网帧头------一层套一层。
SOME/IP报文头12字节,核心字段:
Service ID + Method ID:哪个服务的哪个方法
Length:报文长度
Client ID + Session ID:谁发的、第几次
Message Type:请求/响应/通知/只发不收
Return Code:成功/失败
DoIP是诊断,走13400端口;SOME/IP是通信,走30490端口。诊断是一问一答,通信花样更多。
郭靖合上笔记本:"蓉儿,SOME/IP的底裤我算是扒了一半。那个Service Discovery又是啥?"
黄蓉眨眨眼:"靖哥哥,后面传不传授SOME/IP-SD知识,就看你能不能提供陪我逛街、给我拎包、帮我捶背的服务啦。"
说完,带着一脸调皮转身离开。
郭靖挠挠头,冲着她背影喊:"蓉儿,这些服务......我现在就能提供!"
下篇预告:服务发现如寻宝,不用问路自己找------SOME/IP-SD是个啥?
打完收工,886。
