#01
引 入
SOME/IP(Scalable service-Oriented MiddlewarE over IP)即基于IP的可扩展的面向服务的中间件,其来源可以追溯到由AUTOSAR发布的一种自动/嵌入式通信协议。它是一种面向服务的车载以太网通信协议,位于TCP/UDP之上。
SOME/IP通过明确划分服务提供者(Server)和服务使用者(Client)两个角色,实现了服务的全面管理,包括发布、发现、绑定和调用。该协议不仅支持远程过程调用(RPC)和事件通知功能,还具备底层序列化/线格式的能力,从而有效减轻了总线负载。此外,SOME/IP协议还提供了强大的安全性和可靠性保障机制,能够传输多种数据类型,并展现出良好的可扩展性和灵活性,满足复杂系统的高效通信需求。
#02
协议规范
SOME/IP基于服务提供功能使能。服务可以由零个或多个事件(Event)、方法(Method)和字段(Field)的组合组成。
Event提供从Server到Client的周期性发送或更改时发送的数据。
Method为订阅者提供了发出远程过程调用的可能性,这些调用在Server端执行。
Field是以下三个字段中的一个或多个的组合
一个Notify程序,将提供方的变更数据发送给订阅者
订阅者可以调用的Getter,用于显式查询Server的值
订阅者想要更改Server端的值时可以调用的Setter
Field-Notify程序和Event通知程序之间的主要区别在于,Event仅在需要发送时进行发送,Notify通知程序在订阅后直接发送数据。
1. 协议报头

SOME/IP头格式
2. Message ID(服务ID/方法ID)[占用32bit]
对应用程序方法的RPC调用,或者对通知事件的识别。前16bits表示服务的ID,后面的16bits则是表示Method ID;这两个参数字段均是在服务设计中定义的。
服务的ID的分配由用户/系统设计者决定。但是,消息ID对于整个系统(即车辆)应该是唯一的。
这32bits细分结构如下:
Service ID [16位] | 0[1位] | Method ID [最后15位] |
如果是事件通知,即Event、Notify,则结构如下,Method ID最高bit位为1:
Service ID [16位] | 1[1位] | Event ID [最后15位] |
另外,对于事件通知有个概念叫时间组:Event Group
Event Group是服务内字段的事件和通知事件的逻辑分组,以允许订阅。不得使用Empty事件组。事件(Event)和字段通知(Notify)程序映射到至少一个事件组。
3. Length[占用32bit]
Length字段应包含从下一个字段,即Request ID开始到SOME/IP消息结束的长度,其单位是字节。
4. Request ID (Client ID + Session ID)[占用32bit]
Request ID允许Server和Client区分同一Method、Event、Getter或Setter的多个并行使用。
Request ID仅对提供方和订阅方组合(即一次订阅)是唯一的。
数据生成响应消息时,Server应将Request ID从请求复制到响应消息Response Message中。
Request ID的结构如下:
Client ID [16位] | Session ID [16位] |
AUTOSAR Request ID应由Client ID和Session ID构成,这意味着ECU的实现者可以根据其实现的需要定义Client ID,此ID并非服务ID,而提供者不需要知道此布局或定义,因为他只是在响应中复制完整的请求ID。
Client ID是ECU内呼叫Client的唯一标识符。Client ID允许ECU区分来自多个Client对同一Method的调用。
Client ID还可以通过具有可配置前缀或固定值(例如,客户ID的最高有效字节为诊断地址或给定APP/SW-C的已配置Client ID)来支持在整个车辆中的唯一性。
Client ID前缀[8 bits] | Client ID[8 bits] | 会话Session ID[16 bits] |
会话Session ID是一个唯一标识符,允许将来自同一发送者的连续消息或请求彼此区分开来。
如果Session处理未激活,Session ID应设置为0x00。如果Session处理处于活动状态,Session ID应设置为[0x1,0xFFFF]范围内的值。会话ID应根据各自的用例增加。
请求/响应方法(Request/Response Method)应使用会话ID进行会话处理。会话ID应在每次调用后递增。
当Session ID达到0xFFFF时,它将循环并以0x01重新启动;
对于RR Method,如果响应的Session ID与请求的Session ID不匹配,此时认为此响应并非对应该请求的,必须忽略响应。
对于通知消息,如果会话处理未激活,接收方应忽略会话ID;如果会话处理处于活动状态,接收方应根据各自的用例处理会话ID。
5. Protocal Version[占用8bits]
现在的协议版本应为1
6. Interface Version[占用8bits]
接口版本应为8bit字段,其中包含主服务接口的版本
7. Message Type[占用8bits]
数据消息类型字段用于区分不同类型的消息,并应包含以下值:
数值 | 对应释义 | 描述 |
0x00 | REQUEST | 需要响应的请求(即使无效) |
0x01 | REQUEST_NO_RETURN | fire&forget请求,不需要响应 |
0x02 | NOTIFICATION | 通知/事件回调的请求,不需要响应 |
0x80 | RESPONSE | 响应消息 |
0x81 | 错误ERROR | 包含错误的响应 |
0x20 | TP_REQUEST | 期望响应的TP请求(即使无效) |
0x21 | TP_REQUEST_NO_RETURN | TP fire&forget请求 |
0x22 | TP_NOTIFICATION | 通知/事件回调的TP请求不需要响应 |
0xa0 | TP_RESPONSE | TP响应消息 |
0xa1 | TP_ERROR | 包含错误的TP响应 |
信息类型
当未发生错误时,应通过响应(消息类型0x80)响应常规请求(消息类型0x00)。如果发生错误,应发送错误消息(消息类型0x81)。当然也可以发送没有响应消息的请求(消息类型0x01)。
数据消息类型(=0x20)的第三高位应称为TP标志,并应设置为1,以表示当前SOME/IP消息是一个segment。
8. Return Code[占用8bit]
返回码用于表示请求是否已成功处理。为了简化报头布局,每条消息都传输字段返回代码。下表显示了特定消息类型的允许返回代码:
消息类型 | 允许返回代码 |
REQUEST | N/A set to 0x00 (E_OK) |
REQUEST_NO_RETURN | N/A set to 0x00 (E_OK) |
NOTIFICATION | N/A set to 0x00 (E_OK) |
ERROR | 不应为0x00(E_OK)。 |
9. Payload[长度根据Length数值计算]
SOME/IP有效负载字段的大小取决于所使用的传输协议。具有UDP SOME/IP有效负载应介于0和1400字节之间。限制在1400字节以允许将来对协议栈进行更改。由于TCP支持有效负载的分段,因此可以支持更大的大小。
有效负载可能由Even的数据元素或Method的参数组成。
#03
协议数据的序列化
序列化(Serialization)是指将数据结构或对象状态转换为二进制串(字节数组)的过程。SOME/IP协议在传输过程中也自然需要进行序列化和反序列化。
SOME/IP需要基于通信接口规范定义的参数列表进行序列化。接口规范明确了PDU(协议数据单元)中所有数据结构的精确位置,并且在设计时必须考虑存储器对齐的问题。
对齐操作通过在数据项之后添加填充元素来调整数据的起始位置,以确保数据从特定的、对齐的内存地址开始。这是因为在某些处理器架构中,当数据从某个数的倍数(例如32位的倍数)地址开始时,数据访问会更加高效。
如果可变大小数据不是序列化数据流中的最后一个元素,则应通过在可变大小数据后插入填充元素来实现数据对齐。

数据填充示例
数据固定长度数据元素后面不得有填充物,以确保后面数据对齐。
可变长度数据元素后面的数据对齐应为8、16、32、64、128或256位。
基本数据类型
基本数据类型参数序列化时所占payload如下表所示:
类型 | 描述 | 大小[位] | 注释 |
布尔值 | TRUE/FALSE value | 8 | FALSE (0), TRUE (1) |
uint8 | 无符号整数 | 8 |
|
uint16 | 无符号整数 | 16 |
|
uint32 | 无符号整数 | 32 |
|
uint64 | 无符号整数 | 64 |
|
sint8 | 有符号整数 | 8 |
|
sint16 | 有符号整数 | 16 |
|
sint32 | 有符号整数 | 32 |
|
sint64 | 有符号整数 | 64 |
|
float32 | 浮点数 | 32 | IEEE 754二进制32(单精度) |
float64 | 浮点数 | 64 | IEEE 754二进制64(双精度) |
支持的基本数据类型
固定长度结构体
结构体的序列化应接近内存布局。这意味着,只有参数应按顺序序列化到缓冲区中。特别是对于结构体来说,考虑正确的内存对齐是很重要的,可参考下图的序列方式。

字符串
SOME/IP应支持不同的Unicode编码,包括UTF-8、UTF-16BE和UTF-16LE。UTF-8字符串应以“\0”字符结尾。这意味着它们将以0x00字节结束。UTF-16LE和UTF-16BE字符串应以“\0”字符结尾。这意味着它们应以(至少)两个0x00字节结束。
对于固定长度的字符串,尽管具有固定长度,但数据串应以“\0”字符终止。如果具有固定长度的字符串的长度大于预期值,则应中止反序列化,并将消息视为格式错误。
如果固定长度字符串的长度小于预期值(预期值应基于数据类型定义),并且使用“\0”正确终止,则应接受该字符串。
具有动态长度的数据字符串应以长度字段开头。长度以字节为单位。动态长度字符串的长度字段应为8、16或32位。这应由配置决定。未配置在字符串前面添加的长度字段的长度为32位(长度字段的默认长度)。
枚举
枚举应作为无符号整数数据类型直接序列化传输就好。
#04
通信实例分析
Event消息报文
下面是一个Event通知事件的SOME/IP部分报文,通知参数为一个uint8类型数据;

根据上面的协议规范,可知这个Event消息是属于服务0xb的,对应的事件id是0x3,表示事件id具有16bit位,最高位固定为1,完整的Method ID则是0x8003;
Length字段值为0xa,因此可以解析出payload字段长度为2bytes;
这一个client id标识为0x00,会话id是0x9530;
在报文中找到该连接中的下一条消息,会话id自增1,如下图;

协议版本固定为0x01,接口版本按设计为0x01;消息类型,是0x2,解析为NOTIFICATION,就是Event类型消息;
数据段有2个bytes,也就是通知的值为0x51。
Method RR报文
如下面两图所示,分别代表了一个事件消息的请求和对应该请求的响应报文


请求报文中,可以看出这是0xb服务中,针对0x4 Method方法的请求,具体请求内容可查看通信设计表中0xb服务中0x4方法具体含义,该方法定义了两个uint8的输入,第一个请求参数数值为0x0,第二个请求参数数值为0x1;client-id为0x1d16,session-id为0x00bd,对应在该请求接口响应的消息中的这两个字段是与请求中的对应字段是一致的,回复消息具有一个参数,数据类型为uint8(该类型不是推断出来的,是由通信设计表来的),值为0x0。
文章转载自公众号:汽车电子与软件
作者:不可说
原文链接:https://mp.weixin.qq.com/s/vrf2Gm5a41Ku7iGdOMw_Wg
