# AUTOSAR SOME/IP 序列化规则


# AUTOSAR SOME/IP 序列化规则

----

## 4 协议规范

SOME/IP 提供基于网络的面向服务通信。它基于服务定义，服务定义列出了服务提供的功能。一个服务可以由零个或多个事件、方法和字段组合而成。

- **事件**：提供从服务提供者到订阅者的周期性或变化时发送的数据。
- **方法**：允许订阅者发起远程过程调用（RPC），该调用在服务提供者端执行。
- **字段**：由以下三个部分中的一个或多个组合而成：
    - 通知器：当数据变化时从服务提供者向订阅者发送数据
    - 获取器：可由订阅者调用，显式向服务提供者查询值
    - 设置器：当订阅者想要修改服务提供者端的值时调用

**字段的通知器与事件的主要区别在于：事件仅在数据变化时发送，而字段的通知器在订阅后会立即发送数据。**

## 4.1 SOME/IP 消息格式规范（序列化）

序列化描述了数据在协议数据单元（PDU）中作为 `UDP` 或 `TCP` 报文负载的表示方式，这些报文通过基于 `IP` 的汽车车载网络传输。

### 4.1.1 限制

不支持对 SOME/IP 报文的乱序分段进行重排序。

### 4.1.2 头部

[PRS_SOMEIP_00030] ⌈头部布局结构应包含以下字段：
- 消息 ID（服务 ID/方法 ID）[32 位]
- 长度 [32 位]
- 请求 ID（客户端 ID/会话 ID）[32 位]
- 协议版本 [8 位]
- 接口版本 [8 位]
- 消息类型 [8 位]
- 返回码 [8 位]

⌋

[PRS_SOMEIP_00030] 如图 4.1 所示。


**图 4.1：SOME/IP 头部格式**

[PRS_SOMEIP_00941] ⌈在应用端到端（E2E）通信保护的情况下，E2E 头部应放置在返回码之后，具体位置取决于所选的 E2E 头部偏移值。默认偏移值为 64 位，这会将 E2E 头部恰好放在返回码和负载之间。⌋

[PRS_SOMEIP_00941] 如图 4.2 所示。

**图 4.2：SOME/IP 头部与 E2E 头部格式**

[PRS_SOMEIP_00031] ⌈出于互操作性考虑，所有 SOME/IP 实现的头部布局应完全相同。字段按传输顺序呈现，即左上角的字段最先传输。⌋

#### 4.1.2.1 消息 ID [32 位]

[PRS_SOMEIP_00034] ⌈消息 ID 应为 32 位标识符，用于标识：
- 对应用程序方法的 RPC 调用
- 或标识一个事件

⌋

**注意**：消息 ID 的分配由用户/系统设计者决定。但消息 ID 在整个系统（即车辆）中应是唯一的。

#### 4.1.2.2 方法 ID [15 位]

[PRS_SOMEIP_00038] ⌈方法调用的消息 ID 应按表 4.1 所示的结构组织，支持  $2^{16}$  个服务，每个服务支持  $2^{15}$  个方法。⌋

**表 4.1：ID 结构**

#### 4.1.2.3 事件 ID [15 位]

事件组是服务内部事件和字段通知事件的逻辑分组，用于实现订阅功能。

[PRS_SOMEIP_00040] ⌈事件和通知使用 RPC 传输，事件应按表 4.2 所示的结构组织。

⌋

**表 4.2：事件 ID 结构**

[PRS_SOMEIP_00365] ⌈不应使用空事件组。⌋

[PRS_SOMEIP_00366] ⌈事件以及字段通知器应至少映射到一个事件组。⌋

#### 4.1.2.4 长度 [32 位]

[PRS_SOMEIP_00042] ⌈长度字段应包含从请求 ID/客户端 ID 开始到 SOME/IP 报文结束的字节数。⌋

#### 4.1.2.5 请求 ID [32 位]

请求 ID 允许服务提供者和订阅者区分同一方法、事件、获取器或设置器的多个并行调用。

[PRS_SOMEIP_00043] ⌈请求 ID 仅需在特定的提供者-订阅者组合（即一个订阅）中保持唯一。⌋

[PRS_SOMEIP_00704] ⌈生成响应报文时，服务提供者应将请求 ID 从请求报文复制到响应报文。⌋

**注意**：这允许订阅者即使在有多个未完成请求的情况下，也能将响应映射到对应的请求。

[PRS_SOMEIP_00044] ⌈在响应到达或不再期望收到响应（超时）之前，不得重复使用请求 ID。⌋

**请求 ID 的结构**

[PRS_SOMEIP_00046] ⌈在 AUTOSAR 中，请求 ID 应由客户端 ID 和会话 ID 组成，如表 4.3 所示。

**表 4.3：请求 ID 结构**

⌋

**注意**：这意味着 ECU 的实现者可以根据其实现需求定义客户端 ID，而服务提供者无需了解此布局或定义，因为它只需在响应中复制完整的请求 ID。

[PRS_SOMEIP_00702] ⌈客户端 ID 是 ECU 内部调用客户端的唯一标识符。客户端 ID 允许 ECU 区分来自多个客户端对同一方法的调用。⌋

[PRS_SOMEIP_00703] ⌈会话 ID 是一个唯一标识符，用于区分来自同一发送方的连续报文或请求。⌋

[PRS_SOMEIP_00532] ⌈客户端 ID 还应支持通过可配置前缀或固定值在整个车辆中保持唯一（例如，客户端 ID 的最高有效字节为诊断地址或为特定应用/软件组件配置的客户端 ID）。⌋

**表 4.4：客户端 ID 示例**

[PRS_SOMEIP_00932] ⌈在会话处理未激活的情况下，会话 ID 应设置为 0x00。⌋

[PRS_SOMEIP_00933] ⌈在会话处理激活的情况下，会话 ID 应设置为 [0x1, 0xFFFF] 范围内的值。⌋

[PRS_SOMEIP_00934] ⌈在会话处理激活的情况下，会话 ID 应根据相应的用例递增（有关专用用例的详细信息包含在单独的规范条目中，例如 [PRS_SOMEIP_00533]）。⌋

[PRS_SOMEIP_00533] ⌈请求/响应方法应使用带会话 ID 的会话处理。每次调用后会话 ID 应递增。⌋

[PRS_SOMEIP_00521] ⌈当会话 ID 达到 0xFFFF 时，应回绕并从 0x01 重新开始。⌋

[PRS_SOMEIP_00739] ⌈对于请求/响应方法，如果响应的会话 ID 与请求的会话 ID 不匹配，订阅者应忽略该响应。⌋

[PRS_SOMEIP_00935] ⌈对于通知报文，在会话处理未激活的情况下，接收方应忽略会话 ID。⌋

[PRS_SOMEIP_00936] ⌈对于通知报文，在会话处理激活的情况下，接收方应根据相应的用例处理会话 ID（有关专用用例的详细信息包含在单独的规范条目中，例如 [PRS_SOMEIP_00741]）。⌋

#### 4.1.2.6 协议版本 [8 位]

协议版本标识所使用的 SOME/IP 头部格式（不包括负载格式）。

[PRS_SOMEIP_00052] ⌈协议版本应为 8 位字段，包含 SOME/IP 协议版本号。⌋

[PRS_SOMEIP_00050] ⌈当 SOME/IP 头部发生不兼容变更时，协议版本应递增。如果基于旧协议版本的接收方不会丢弃报文并错误地处理它，则该变更是不兼容的。⌋

**注意**：报文处理和错误处理在第 4.2.6.3 节（错误处理概述）中定义。

**注意**：协议版本本身是 SOME/IP 头部的一部分，因此协议版本在头部中的位置不得更改。

**注意**：仅影响负载格式的变更不应递增协议版本。

[PRS_SOMEIP_00051] ⌈协议版本应为 1。⌋

#### 4.1.2.7 接口版本 [8 位]

[PRS_SOMEIP_00053] ⌈接口版本应为 8 位字段，包含服务接口的主版本号。⌋

#### 4.1.2.8 消息类型 [8 位]

[PRS_SOMEIP_00055] ⌈消息类型字段用于区分不同类型的报文，应包含表 4.5 所示的值。⌋

|数值|值|描述|
|---|---|---|
|0x00|REQUEST|期望响应的请求（即使返回 void）|
|0x01|REQUEST_NO_RETURN|发后即忘请求|
|0x02|NOTIFICATION|通知/事件回调请求，不期望响应|
|0x80|RESPONSE|响应报文|
|0x81|ERROR|包含错误的响应|
|0x20|TP_REQUEST|期望响应的 TP 请求（即使返回 void）|
|0x21|TP_REQUEST_NO_RETURN|TP 发后即忘请求|
|0x22|TP_NOTIFICATION|TP 通知/事件回调请求，不期望响应|
|0xa0|TP_RESPONSE|TP 响应报文|
|0xa1|TP_ERROR|包含错误的 TP 响应|
**表 4.5：消息类型**

[PRS_SOMEIP_00701] ⌈常规请求（消息类型 0x00）在无错误发生时应由响应（消息类型 0x80）应答。如果发生错误，应发送错误报文（消息类型 0x81）。⌋

也可以发送不包含响应报文的请求（消息类型 0x01）。对于通过通知更新值，存在一个回调接口（消息类型 0x02）。

[PRS_SOMEIP_00367] ⌈消息类型的第 3 高位（= 0x20）应称为 TP 标志，设置为 1 表示当前 SOME/IP 报文是一个分段。消息类型的其他位按本节规定设置。⌋

**注意**：请求（0x00）类型的分段消息类型为 0x20，响应（0x80）类型的分段消息类型为 0xa0，依此类推。详情参见（第 4.2.1.4 节）。

#### 4.1.2.9 返回码 [8 位]

[PRS_SOMEIP_00058] ⌈返回码用于指示请求是否成功处理。为简化头部布局，每个报文都携带返回码字段。特定消息类型允许的返回码如表 4.6 所示。⌋

|消息类型|允许的返回码|
|---|---|
|REQUEST|不适用，设置为 0x00（E_OK）|
|REQUEST_NO_RETURN|不适用，设置为 0x00（E_OK）|
|NOTIFICATION|不适用，设置为 0x00（E_OK）|
|RESPONSE|参见 [PRS_SOMEIP_00191] 中的返回码|
|ERROR|参见 [PRS_SOMEIP_00191] 中的返回码，不得为 0x00（E_OK）|
**表 4.6：特定消息类型允许的返回码**

#### 4.1.2.10 负载 [可变长度]

负载字段携带参数。参数的序列化将在以下章节中说明。

SOME/IP 负载字段的大小取决于所使用的传输协议。使用 UDP 时，SOME/IP 负载应在 0 到 1400 字节之间。限制为 1400 字节是为了允许协议栈未来的变更（例如切换到 IPv6 或添加安全机制）。由于 TCP 支持负载分段，因此自动支持更大的大小。

负载可能包含事件的数据元素或方法的参数。

### 4.1.3 字节序

[PRS_SOMEIP_00368] ⌈SOME/IP 头部应采用网络字节序（大端）编码。⌋

[PRS_SOMEIP_00369] ⌈负载内部参数的字节序应由配置定义。⌋

### 4.1.4 数据结构的序列化

序列化基于接口规范定义的参数列表。接口规范定义了 PDU 中所有数据结构的确切位置，并且必须考虑内存对齐。

对齐通过在数据后插入填充元素来实现，以确保对齐后的数据从特定内存地址开始。

有些处理器架构在数据从特定数字的倍数地址（例如 32 位的倍数）开始时，能够更高效地访问数据（即主存访问）。

[PRS_SOMEIP_00611] ⌈如果可变长度数据不是序列化数据流中的最后一个元素，则应通过在可变长度数据后插入填充元素来实现数据对齐。⌋

**注意**：填充值未定义。

**示例：包含 5 个成员的结构**

- 成员 1：UINT16
- 成员 2：包含 uint8 元素的一维可变长度数组

- 成员 3：UINT32

- 成员 4：UINT64

- 成员 5：包含 uint8 元素的一维可变长度数组

**图 4.3：SOME/IP 填充示例 01**

**示例：包含 5 个成员的结构**

- 成员 1：UINT16

- 成员 2：包含 uint8 元素的一维可变长度数组

- 成员 3：UINT32

- 成员 4：UINT64

- 成员 5：包含 uint8 元素的一维可变长度数组

**图 4.4：SOME/IP 填充示例 02**

[PRS_SOMEIP_00569] ⌈对齐应始终从 SOME/IP 报文的起始位置开始计算。⌋

[PRS_SOMEIP_00612] ⌈固定长度数据元素后不应添加填充来确保后续数据的对齐。⌋

**注意**：如果需要在固定长度数据元素后添加填充，则必须在数据类型定义中明确考虑。

[PRS_SOMEIP_00613] ⌈可变长度数据元素后的数据对齐应为 8、16、32、64、128 或 256 位。⌋

#### 4.1.4.1 基本数据类型

[PRS_SOMEIP_00065] ⌈应支持表 4.7 所示的基本数据类型：⌋

|类型|描述|大小 [位]|备注|
|---|---|---|---|
|boolean|TRUE/FALSE 值|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 binary32（单精度）|
|float64|浮点数|64|IEEE 754 binary64（双精度）|

**表 4.7：支持的基本数据类型**

每个参数的字节序由配置指定。

[PRS_SOMEIP_00615] ⌈对于布尔值的计算，仅解释 `uint8` 的最低位，其余位忽略。⌋

#### 4.1.4.2 结构化数据类型（结构体）

结构体的序列化应接近内存中的布局。这意味着仅将参数按顺序序列化到缓冲区中。特别是对于结构体，考虑正确的内存对齐非常重要。

**图 4.5：结构体的序列化**

[PRS_SOMEIP_00077] ⌈`SOME/IP` 实现不应自动插入虚拟/填充数据。⌋

[PRS_SOMEIP_00079] ⌈根据配置，可以在结构体前插入 `8`、`16` 或 `32` 位的可选长度字段。⌋

[PRS_SOMEIP_00370] ⌈结构体的长度字段应描述该结构体在 SOME/IP 传输中占用的字节数。⌋

[PRS_SOMEIP_00371] ⌈如果长度大于数据类型定义中指定的结构体长度，则仅解释数据类型中指定的字节，其余字节根据长度字段跳过。⌋

[PRS_SOMEIP_00900] ⌈如果长度小于所有结构体成员长度之和，且接收方无法在本地提供缺失数据的替代值，则应中止反序列化，并将该报文视为格式错误。⌋

[PRS_SOMEIP_00712] ⌈结构体的序列化应遵循结构化数据类型的深度优先遍历。⌋

#### 4.1.4.3 带标识符和可选成员的结构化数据类型与参数

为了增强前向和后向兼容性，可以在结构体成员或方法参数前添加额外的数据 ID。然后接收方可以跳过未知的成员/参数（即数据 ID 未知的成员/参数）。当在序列化字节流中传输数据 ID 时，可以在任意位置添加新的成员/参数。

此外，使用数据 ID 允许定义带有可选成员/参数的结构体和方法。成员/参数是否可选在数据定义中指定。

可选成员/参数是否实际存在于结构体/方法中，必须在运行时确定。如何实现取决于所使用的编程语言或软件平台（例如使用特殊的可用标志、使用特殊方法、使用可能为 null 的指针等）。

[PRS_SOMEIP_00201] ⌈数据 ID 在结构体的直接成员或方法的参数范围内应是唯一的。⌋

**注意**：数据 ID 不需要在不同结构体或方法之间保持唯一。

**注意**：目前，AUTOSAR 方法论、AUTOSAR CP RTE 以及 AUTOSAR AP ara::com 均不支持定义或使用可选方法参数。

[PRS_SOMEIP_00230] ⌈数据 ID 应要么为结构体同一层级的所有成员定义，要么不为任何成员定义。⌋

[PRS_SOMEIP_00231] ⌈数据 ID 应要么为方法的所有参数定义，要么不为任何参数定义。⌋

除了数据 ID 之外，线类型（wire type）编码后续成员的数据类型。数据 ID 和线类型编码在所谓的标签（tag）中。

[PRS_SOMEIP_00202] ⌈标签的长度应为两个字节。⌋

[PRS_SOMEIP_00203] ⌈标签应包含：
- 保留位（第一个字节的第 7 位）
- 线类型（第一个字节的第 6-4 位）
- 数据 ID（第一个字节的第 3-0 位和第二个字节的第 7-0 位）

⌋

标签的布局参见图 4.6。第 7 位是字节的最高有效位，第 0 位是最低有效位。

```Plain Text

线类型 数据 ID（高有效部分） 数据 ID（低有效部分） 长度字段（8/16/32 位） 成员数据 ...
字节 n 字节 n + 1 字节 n + 2 ...
```

**图 4.6：标签布局**

[PRS_SOMEIP_00204] ⌈成员数据 ID 的低有效部分应编码在标签第二个字节的第 7-0 位。成员数据 ID 的高有效部分应编码在第一个字节的第 3-0 位。⌋

**示例**：

成员的数据 ID 为 0x04F2。则第一个字节的第 3-0 位设置为 0x4，第二个字节设置为 0xF2。

[PRS_SOMEIP_00205] ⌈线类型应确定后续成员数据的类型。值的分配如表 4.8 所示。⌋

|线类型|后续数据|
|---|---|
|0|8 位数据 基本数据类型|
|1|16 位数据 基本数据类型|
|2|32 位数据 基本数据类型|
|3|64 位数据 基本数据类型|
|4|复杂数据类型：数组、结构体、字符串、联合体，带有静态大小的长度字段（在数据定义中配置）|
|5|复杂数据类型：数组、结构体、字符串、联合体，带有 1 字节大小的长度字段（忽略静态定义）|
|6|复杂数据类型：数组、结构体、字符串、联合体，带有 2 字节大小的长度字段（忽略静态定义）|
|7|复杂数据类型：数组、结构体、字符串、联合体，带有 4 字节大小的长度字段（忽略静态定义）|

**表 4.8：线类型**

**注意**：线类型 4 确保与当前方法兼容，其中长度字段的大小是静态配置的。这种方法的缺点是，在接口演进过程中更改长度字段的大小总是不兼容的。因此，线类型 5、6 和 7 允许在传输的字节流中编码所使用的长度字段的大小。如果静态配置的长度字段大小不足以容纳当前数据结构的大小，序列化器可以使用此功能。

[PRS_SOMEIP_00206] ⌈如果线类型设置为 5、6 或 7，则应忽略数据定义中定义的长度字段大小，并根据线类型选择长度字段的大小。⌋

如果为结构体的成员/方法的参数配置了数据 ID，则应在序列化字节流中插入一个标签。

**注意**：关于数据 ID 的存在性，参见 [PRS_SOMEIP_00230] 和 [PRS_SOMEIP_00231]。

[PRS_SOMEIP_00212] ⌈如果序列化成员/参数的数据类型是基本数据类型（线类型 0-3）且配置了数据 ID，则标签应直接插入在成员/参数之前。不应在序列化流中插入长度字段。⌋

[PRS_SOMEIP_00213] ⌈如果序列化成员/参数的数据类型不是基本数据类型（线类型 4-7）且配置了数据 ID，则标签应插入在长度字段之前。⌋

[PRS_SOMEIP_00214] ⌈如果序列化成员/参数的数据类型不是基本数据类型且配置了数据 ID，则应始终在成员/参数之前插入长度字段。⌋

**原理**：

长度字段是在反序列化过程中跳过未知成员/参数所必需的。

[PRS_SOMEIP_00221] ⌈长度字段应始终包含到结构体下一个标签的长度。⌋

[PRS_SOMEIP_00208] ⌈如果成员本身是结构体类型，则应恰好有一个长度字段。长度字段根据要求 [PRS_SOMEIP_00079] 和 [PRS_SOMEIP_00370] 添加。⌋

[PRS_SOMEIP_00225] ⌈如果成员本身是动态长度字符串类型，则应恰好有一个长度字段。长度字段根据要求 [PRS_SOMEIP_00089]、[PRS_SOMEIP_00090]、[PRS_SOMEIP_00093]、[PRS_SOMEIP_00094] 和 [PRS_SOMEIP_00095] 添加。⌋

[PRS_SOMEIP_00224] ⌈如果成员本身是固定长度字符串类型，则应恰好有一个与动态长度字符串对应的长度字段。⌋

**注意**：不带标签序列化时，固定长度字符串没有长度字段。带标签序列化时，固定长度字符串也需要与动态长度字符串相同方式的长度字段。

[PRS_SOMEIP_00227] ⌈如果成员本身是动态长度数组类型，则应恰好有一个长度字段。长度字段根据要求 [PRS_SOMEIP_00376]、[PRS_SOMEIP_00107]、[PRS_SOMEIP_00377] 添加，大小为 8、16 或 32 位。⌋

[PRS_SOMEIP_00226] ⌈如果成员本身是固定长度数组类型，则应恰好有一个与动态长度数组对应的长度字段。⌋

[PRS_SOMEIP_00228] ⌈如果成员本身是联合体类型，则应恰好有一个长度字段。长度字段根据要求 [PRS_SOMEIP_00119]、[PRS_SOMEIP_00121] 添加，大小为 8、16 或 32 位。⌋

[PRS_SOMEIP_00229] ⌈如果成员本身是联合体类型，则长度字段应覆盖类型字段、数据和填充字节的大小。⌋

**注意**：不带标签序列化时，联合体的长度字段不覆盖类型字段（参见 [PRS_SOMEIP_00126]）。带标签序列化时，要求长度字段覆盖序列化联合体的完整内容。

[PRS_SOMEIP_00210] ⌈非可扩展（标准）结构体的成员如果是可扩展结构体类型，则应按照可扩展结构体的要求进行序列化。⌋

[PRS_SOMEIP_00211] ⌈可扩展结构体的成员如果是非可扩展（标准）结构体类型，则应按照标准结构体的要求进行序列化。⌋

[PRS_SOMEIP_00222] ⌈根据 [PRS_SOMEIP_00611] 的可变长度数据对齐应始终为 8 位。⌋

**原理**：

当使用大于 8 位的对齐时，序列化器可能会在可变长度数据后添加填充字节。这些填充字节不被长度字段覆盖。如果接收方不知道成员的数据 ID，它也不知道这是可变长度数据以及可能存在填充字节。

[PRS_SOMEIP_00241] ⌈数组、结构体、联合体和字符串的长度字段大小在配置中应大于 0。⌋

**原理**：

TLV 序列化需要使用长度字段。当使用线类型 4 时，长度字段大小必须静态配置。当使用线类型 5-7（动态长度字段大小）时，也必须存在长度字段大小的静态配置，因为并非所有长度字段前面都有标签，例如数组中包含的结构体或 SOME/IP 事件中包含的顶级结构体。此处不使用长度字段会导致歧义。

[PRS_SOMEIP_00242] ⌈数组、结构体、联合体和字符串的配置长度字段大小应相同。⌋

**原理**：

在存在未知成员或参数的情况下，当使用线类型 4 时，反序列化器无法确定成员/参数的实际数据类型。

[PRS_SOMEIP_00243] ⌈长度字段大小应为顶级结构体或方法请求/响应配置。结构体中使用的所有数组、联合体、结构体和字符串，或方法中的所有参数，都应从顶级定义继承长度字段的大小。⌋

**原理**：

在存在未知成员或参数的情况下，当使用线类型 4 时，反序列化器需要知道长度字段的大小。最简单的方法是仅在顶级元素定义长度字段的大小。

[PRS_SOMEIP_00244] ⌈不允许在下级数组、联合体、结构体或字符串，或单个方法参数上覆盖长度字段的大小。⌋

[PRS_SOMEIP_00216] ⌈如果可选成员/参数被标记为不可用，则序列化器不应将其包含在序列化字节流中。⌋

[PRS_SOMEIP_00223] ⌈反序列化器应忽略序列化字节流中不存在的可选成员/参数。⌋

[PRS_SOMEIP_00217] ⌈如果反序列化器读取到未知的数据 ID（即其数据定义中不包含的 ID），则应使用线类型和长度字段的信息跳过该未知成员/参数。⌋

[PRS_SOMEIP_00218] ⌈如果反序列化器在序列化字节流中找不到其数据定义中定义的必需（即非可选）成员/参数，则应中止反序列化，并将该报文视为格式错误。⌋

[PRS_SOMEIP_00220] ⌈如果为以前未使用标签的现有服务接口引入带标签的序列化，则应递增主接口版本以指示此变更。⌋

**注意**：接收方仅处理与消息 ID、协议版本、接口版本和消息类型的所有配置值匹配的接收报文（参见 [PRS_SOMEIP_00195]）。

**带标签结构体序列化示例**

```C
struct myStruct {
    uint8 a; /* 数据 ID = 0 */
    uint8 b; /* 数据 ID = 1 */
    struct c { /* 数据 ID = 2 */
        uint8 c1;
        uint8 c2;
        struct c3 {
            uint8 c31;
            struct c32 {
                uint8 c321;
                uint8[] c322;
                uint8 c323;
            } c32;
            uint8 c33;
        } c3;
        uint8[] c4;
        uint8 c5;
    } c;
    uint8 d; /* 数据 ID = 3 */
    struct e { /* 数据 ID = 4 */
        uint8 e1;
        uint8 e2;
    } e;
    uint8 f; /* 数据 ID = 5 */
    uint8 g; /* 数据 ID = 6 */
};
```

**图 4.7：带标签结构体序列化示例 01**

**图 4.8：带标签结构体序列化示例 02**

**注意**：在图 4.7、图 4.8 和图 4.9 的示例中，顶级结构体有一个长度字段，因为假设长度字段的大小是静态配置的。

```C
struct myStruct {
    uint8 a;
    uint8 b;
    struct c {
        uint8 c1;
        uint8 c2;
        struct c3 {
            uint8 c31; /* 数据 ID = 0 */
            struct c32 { /* 数据 ID = 2 */
                uint8 c321;
                uint8[] c322;
                uint8 c323;
            } c32;
            uint8 c33; /* 数据 ID = 1 */
        } c3;
        uint8[] c4;
        uint8 c5;
    } c;
    uint8 d;
    struct e {
        uint8 e1;
        uint8 e2;
    } e;
    uint8 f;
    uint8 g;
};
```

**图 4.9：带标签结构体序列化示例 03**

根据 [PRS_SOMEIP_00243]，长度字段的大小为顶级结构体配置，并传递给子元素。因此，顶级结构体本身根据 [PRS_SOMEIP_00925] 和 [PRS_SOMEIP_00926] 具有一个长度字段。

如果长度字段的大小不是静态配置的（即使用线类型 5-7），则顶级结构体将没有长度字段。

**带标签参数序列化示例**

```C

uint8 myFunction (
    IN uint8 a, /* 数据 ID = 0 */
    IN uint8 b, /* 数据 ID = 1 */
    IN myStruct c, /* 数据 ID = 2 */
    OUT uint32 d, /* 数据 ID = 0 */
    OUT uint8 e /* 数据 ID = 1 */
);
```

**图 4.10：带标签参数序列化示例**

**注意**：在图 4.10 的示例中，SOME/IP 头部结束和第一个标签之间没有额外的长度字段。这与 SOME/IP 头部中的消息长度字段是冗余的。

#### 4.1.4.4 字符串

以下要求适用于固定长度和动态长度字符串。

[PRS_SOMEIP_00372] ⌈应支持不同的 Unicode 编码，包括 UTF-8、UTF-16BE 和 UTF-16LE。⌋

[PRS_SOMEIP_00084] ⌈UTF-16LE 和 UTF-16BE 字符串应以 "\0" 字符零终止。这意味着它们应以（至少）两个 0x00 字节结尾。⌋

[PRS_SOMEIP_00085] ⌈UTF-16LE 和 UTF-16BE 字符串应具有偶数长度。⌋

[PRS_SOMEIP_00086] ⌈如果 UTF-16LE 和 UTF-16BE 字符串具有奇数长度，则最后一个字节应被忽略。前两个字节应为 0x00 字节（终止符）。⌋

[PRS_SOMEIP_00087] ⌈所有字符串应始终以字节顺序标记（BOM）开头。BOM 应包含在固定长度字符串和动态长度字符串中。BOM 允许检测所使用的编码。⌋

##### 4.1.4.4.1 字符串（固定长度）

[PRS_SOMEIP_00373] ⌈尽管具有固定长度，字符串仍应以 "`\0`" 字符终止。⌋

[PRS_SOMEIP_00374] ⌈字符串的长度（包括 "`\0`"）以字节为单位，必须在数据类型定义中指定。使用 "`\0`" 填充未使用的空间。`BOM` 包含在长度中。⌋

[PRS_SOMEIP_00911] ⌈如果固定长度字符串的长度大于预期（预期基于数据类型定义），则应中止反序列化，并将该报文视为格式错误。⌋

[PRS_SOMEIP_00912] ⌈如果固定长度字符串的长度小于预期（预期基于数据类型定义）且正确使用 "`\0`" 终止，则应接受该字符串。⌋

[PRS_SOMEIP_00913] ⌈如果固定长度字符串的长度小于预期（预期基于数据类型定义）且未正确使用 "`\0`" 终止，则应中止反序列化，并将该报文视为格式错误。⌋

##### 4.1.4.4.2 字符串（动态长度）

[PRS_SOMEIP_00089] ⌈动态长度字符串应以长度字段开头。长度以字节为单位测量。⌋

[PRS_SOMEIP_00090] ⌈长度字段放置在 `BOM` 之前，`BOM` 包含在长度中。⌋

[PRS_SOMEIP_00091] ⌈字符串以 "`\0`" 终止。⌋

**注意**：字符串的最大字节数（包括 "`\0`" 终止符）也应从数据类型定义中推导。

[PRS_SOMEIP_00092] ⌈[PRS_SOMEIP_00084]、[PRS_SOMEIP_00085] 和 [PRS_SOMEIP_00086] 也适用于动态长度字符串。⌋

[PRS_SOMEIP_00093] ⌈动态长度字符串应具有 `8`、`16` 或 `32` 位的长度字段。这应由配置确定。⌋

[PRS_SOMEIP_00094] ⌈如果未配置，则添加在字符串前面的长度字段的长度为 `32` 位（默认长度字段长度）。⌋

[PRS_SOMEIP_00095] ⌈字符串长度字段的长度不包含在长度字段的值中；即长度字段不计算自身。⌋

[PRS_SOMEIP_00914] ⌈如果可变长度字符串的长度大于预期（预期基于数据类型定义），则应中止反序列化，并将该报文视为格式错误。⌋

除了将应用程序字符串作为带有 `BOM` 和 "`\0`" 终止符的 `SOME/IP` 字符串传输外，字符串也可以作为不带 `BOM` 和 "`\0`" 终止符的纯动态长度数组传输（参见第 4.1.4.5.2 节）。请注意，这要求在应用程序中完成完整的字符串处理（例如字节序转换）。

这也可以通过将属性 `implementsLegacyStringSerialization` 设置为 `true` 来实现。在 `CP` 中，此属性在 `SOMEIPTransformationISignalProps` 中配置；在 `AP` 中，此属性在 `ApSomeipTransformationProps` 中配置。

**注意！** 此属性不具备未来安全性，将在即将发布的 AUTOSAR 版本中删除！

因此，为了向前兼容，在这种情况下应优先使用纯动态长度数组。

#### 4.1.4.5 数组

##### 4.1.4.5.1 数组（固定长度）

固定长度数组更适合在非常小的设备中使用。动态长度数组可能会在使用它们的 `ECU` 上需要更多资源。

[PRS_SOMEIP_00944] ⌈固定长度数组可以以可选长度字段开头。⌋

[PRS_SOMEIP_00917] ⌈如果固定长度数组的长度大于预期（预期基于数据类型定义），则仅解释数据类型中指定的元素，其余字节根据长度字段跳过。⌋

[PRS_SOMEIP_00918] ⌈如果固定长度数组的长度小于预期（预期基于数据类型定义）且接收方无法在本地提供缺失数据的替代值，则应中止反序列化，并将该报文视为格式错误。⌋

**注意**：固定大小数组的溢出只能通过长度字段检测。

**一维数组**

[PRS_SOMEIP_00099] ⌈固定长度为 "n" 的一维数组应恰好携带 "n" 个相同类型的元素。可选长度字段可以在第一个元素之前（参见 [PRS_SOMEIP_00944]）。⌋

**注意**：如果为特定固定长度数组定义了长度字段，则该数组在总线上表示为长度字段和 n 个相同数据类型元素的集合。

[PRS_SOMEIP_00099] 的布局如图 4.11 所示。

**图 4.11：一维数组（固定长度）**

**多维数组**

[PRS_SOMEIP_00101] ⌈多维数组的序列化遵循编程语言中多维数组的内存布局（行优先顺序）。⌋

**注意**：如果为特定多维固定长度数组定义了长度字段，则该数组在总线上表示为长度字段和 n 个集合的组合，每个集合由长度字段和 m 个相同数据类型的元素组成。

[PRS_SOMEIP_00101] 的布局如图 4.12 所示。

**图 4.12：多维数组（固定长度）**

##### 4.1.4.5.2 动态长度数组

[PRS_SOMEIP_00375] ⌈动态长度数组的布局应基于固定长度数组的布局。⌋

[PRS_SOMEIP_00376] ⌈应使用数组开头的可选长度字段来指定数组的长度（以字节为单位）。⌋

[PRS_SOMEIP_00107] ⌈长度字段的长度应为 0、8、16 或 32 位。这应由配置确定。⌋

[PRS_SOMEIP_00377] ⌈长度不包括长度字段本身的大小。⌋

**注意**：如果长度字段的长度设置为 0 位，则数组中的元素数量必须固定；因此，它是一个固定长度数组。

动态数组的布局如图 4.13 和图 4.14 所示。

**图 4.13：一维数组（动态长度）**

在一维数组中，使用一个长度字段，该字段携带数组使用的字节数。

静态长度元素的数量可以通过除以元素大小轻松计算。

在动态长度元素的情况下，无法计算元素数量，但必须顺序解析元素。

图 4.14 显示了动态长度多维数组的结构。

**图 4.14：多维数组（动态长度）**

[PRS_SOMEIP_00114] ⌈在多维数组中，不同维度的每个子数组都应有自己的长度字段。⌋

如果需要静态缓冲区大小分配，则数据类型定义应定义每个维度的最大长度。

**原理**：当以字节为单位测量长度时，复杂的多维数组可以在反序列化中被跳过。

SOME/IP 还支持同一维度中列和行的不同长度。参见图 4.14 中的 k_1 和 k_2。每个动态长度数组前面都需要有一个长度指示器。这适用于外部数组和所有内部/嵌套数组。

[PRS_SOMEIP_00919] ⌈如果可变长度数组的长度大于预期（预期基于数据类型定义），则仅解释数据类型中指定的元素，其余字节根据长度字段跳过。⌋

[PRS_SOMEIP_00945] ⌈如果未配置，则添加在动态长度数组前面的长度字段的长度为 32 位（默认长度字段长度）。⌋

#### 4.1.4.6 枚举

[PRS_SOMEIP_00705] ⌈SOME/IP 不考虑枚举。枚举应作为无符号整数数据类型传输。⌋

#### 4.1.4.7 位域

[PRS_SOMEIP_00300] ⌈位域应作为无符号数据类型 uint8/uint16/uint32 传输。⌋

数据类型定义将能够定义每个位的名称和值。

#### 4.1.4.8 联合体 / 变体

在网络上定义数据为联合体的用例中，负载可以是不同的数据类型。

联合体（也称为变体）是这样一种参数，它可以包含不同类型的数据。例如，如果定义了一个 uint8 和 uint16 类型的联合体，则该联合体应携带 uint8 或 uint16 类型的数据。

在负载中传输哪种数据类型只能在执行期间决定。然而，在这种情况下，不仅需要发送数据本身，还需要添加关于适用数据类型的信息作为传输的"元数据"。

通过附加的元数据，发送方可以识别联合体的适用数据类型，接收方可以相应地正确访问数据。

[PRS_SOMEIP_00118] ⌈联合体应用于通过网络传输具有替代数据类型的数据。⌋

[PRS_SOMEIP_00119] ⌈联合体应由长度字段、类型选择器和负载组成，如表 4.9 所示：⌋

|长度字段 [32 位]|
|---|
|类型字段 [32 位]|
|包含填充的数据 [sizeof(填充) = 长度 - sizeof(数据)]|
**表 4.9：联合体的默认序列化**

[PRS_SOMEIP_00126] ⌈长度字段应定义数据和填充的字节大小，不包括长度字段和类型字段的大小。⌋

**注意**：如果相应配置，填充可用于在序列化数据流中对齐后续数据。

[PRS_SOMEIP_00121] ⌈长度字段的长度应由配置定义，应为 32、16、8 或 0 位。⌋

[PRS_SOMEIP_00122] ⌈长度字段长度为 0 位表示不会向 PDU 写入长度字段。⌋

[PRS_SOMEIP_00123] ⌈如果长度字段的长度为 0 位，则联合体中的所有类型应具有相同的长度。⌋

[PRS_SOMEIP_00129] ⌈类型字段应指定数据的数据类型。⌋

[PRS_SOMEIP_00127] ⌈类型字段的长度应由配置定义，应为 32、16 或 8 位。⌋

[PRS_SOMEIP_00906] ⌈类型字段的可能值应为每个联合体单独配置定义。⌋

[PRS_SOMEIP_00907] ⌈类型字段的值 0 应保留给 NULL 类型。⌋

**注意**：这表示一个空联合体。

[PRS_SOMEIP_00908] ⌈联合体是否允许 NULL 应由配置定义。⌋

[PRS_SOMEIP_00130] ⌈负载根据类型字段中的类型进行序列化。⌋

在以下示例中，长度字段的长度指定为 32 位。联合体应支持 uint8 和 uint16 作为数据。两者都填充到 32 位边界（长度 = 4 字节）。

uint8 的序列化如表 4.10 所示。

|长度 = 4 字节||||
|---|---|---|---|
|类型 = 1||||
|uint8|填充 0x00|填充 0x00|填充 0x00|

**表 4.10：示例：uint8**

uint16 的序列化如表 4.11 所示。

|长度 = 4 字节|||
|---|---|---|
|类型 = 2|||
|uint16|填充 0x00|填充 0x00|

**表 4.11：示例：uint16**

[PRS_SOMEIP_00915] ⌈如果联合体的长度大于预期（预期基于数据类型定义），则仅解释数据类型中指定的字节，其余字节根据长度字段跳过。⌋

[PRS_SOMEIP_00916] ⌈如果联合体的长度小于预期（预期基于数据类型定义），则取决于内部数据类型是否可以反序列化有效数据，或者应中止反序列化并将该报文视为格式错误。⌋

## 4.2 SOME/IP 协议规范

本章描述 SOME/IP 的远程过程调用（RPC）、事件通知和错误处理。

### 4.2.1 传输协议绑定

为了传输 SOME/IP 报文，可以使用不同的传输协议。SOME/IP 目前支持 UDP 和 TCP。它们的绑定在以下章节中说明，而第 6 章讨论应选择哪种传输协议。

[PRS_SOMEIP_00138] ⌈如果服务器运行同一服务的不同实例，则属于不同服务实例的报文应通过服务器端的传输协议端口映射到相应的服务实例。⌋

详情参见第 4.2.1.3 节。

[PRS_SOMEIP_00535] ⌈所有传输协议绑定都应支持在一个传输层 PDU（即 UDP 数据包或 TCP 段）中传输多个 SOME/IP 报文。⌋

[PRS_SOMEIP_00142] ⌈接收方的 SOME/IP 实现应能够接收通过 UDP 或 TCP 传输的未对齐 SOME/IP 报文。⌋

**原理**：

当在 UDP 或 TCP 中传输多个 SOME/IP 负载时，只有当每个负载的长度是对齐大小（例如 32 位）的倍数时，才能保证负载的对齐。

[PRS_SOMEIP_00140] ⌈头部格式允许在单个数据包中传输多个 SOME/IP 报文。SOME/IP 实现应通过 SOME/IP 长度字段识别 SOME/IP 报文的结束。基于数据包长度字段，SOME/IP 应确定数据包中是否还有其他 SOME/IP 报文。这适用于 UDP 和 TCP 传输。⌋

[PRS_SOMEIP_00141] ⌈每个 SOME/IP 负载都应有自己的 SOME/IP 头部。⌋

[PRS_SOMEIP_00940] ⌈一个服务实例可以使用以下设置来传输其所有方法、事件和通知：
- 最多一个 TCP 连接
- 最多一个 UDP 单播连接
- 最多一个 UDP 多播连接

⌋

#### 4.2.1.1 UDP 绑定

[PRS_SOMEIP_00139] ⌈SOME/IP 的 UDP 绑定应通过在 UDP 数据包中传输 SOME/IP 报文来实现。⌋

[PRS_SOMEIP_00137] ⌈SOME/IP 协议不应限制 UDP 分片的使用。⌋

[PRS_SOMEIP_00943] ⌈对于配置为使用 UDP 单播通信的服务实例的所有方法、事件和通知，客户端和服务器应使用单个 UDP 单播连接。⌋

[PRS_SOMEIP_00942] ⌈对于配置为使用 UDP 多播通信的服务实例的所有事件和通知，客户端和服务器应使用单个 UDP 多播连接。⌋

#### 4.2.1.2 TCP 绑定

SOME/IP 的 TCP 绑定在很大程度上基于 UDP 绑定。与 UDP 绑定不同，TCP 绑定允许更大的 SOME/IP 报文，并使用 TCP 的健壮性特性（处理丢失、重排序、重复等）。

为了降低延迟和响应时间，应关闭 Nagle 算法（TCP_NODELAY）。

[PRS_SOMEIP_00706] ⌈当 TCP 连接丢失时，未完成的请求应作为超时处理。⌋ 由于 TCP 处理可靠性，因此不需要额外的可靠性手段。

[PRS_SOMEIP_00707] ⌈对于配置为使用 TCP 通信的服务实例的所有方法、事件和通知，客户端和服务器应使用单个 TCP 连接。⌋

[PRS_SOMEIP_00708] ⌈当需要传输第一个方法调用或客户端尝试接收第一个通知时，TCP 连接应由客户端打开。⌋

客户端负责在 TCP 连接失败时重新建立连接。

[PRS_SOMEIP_00709] ⌈当不再需要 TCP 连接时，TCP 连接应由客户端关闭。⌋

[PRS_SOMEIP_00710] ⌈当使用该 TCP 连接的所有服务都不再可用（已停止或超时）时，TCP 连接应由客户端关闭。⌋

[PRS_SOMEIP_00711] ⌈当停止所有服务时，服务器不应停止 TCP 连接。给客户端足够的时间处理控制数据以自行关闭 TCP 连接。⌋

**原理**：

如果服务器在客户端意识到不再需要 TCP 之前关闭连接，客户端将尝试重新建立 TCP 连接。

**使用 Magic Cookie 实现 TCP 流重新同步**

[PRS_SOMEIP_00154] ⌈为了允许测试工具识别通过 TCP 传输的 SOME/IP 报文的边界，可以在 TCP 报文流中的 SOME/IP 报文之间定期插入 SOME/IP Magic Cookie 报文。⌋

[PRS_SOMEIP_00160] ⌈Magic Cookie 报文的布局应包含以下字段：

- 客户端到服务器的通信：
    - 消息 ID（服务 ID/方法 ID）：0xFFFF 0000
    - 长度：0x0000 0008
    - 请求 ID（客户端 ID/会话 ID）：0xDEAD BEEF
    - 协议版本：0x01
    - 接口版本：0x01
    - 消息类型：0x01
    - 返回码：0x00
- 服务器到客户端的通信：
    - 消息 ID（服务 ID/方法 ID）：0xFFFF 8000
    - 长度：0x0000 0008
    - 请求 ID（客户端 ID/会话 ID）：0xDEAD BEEF
    - 协议版本：0x01
    - 接口版本：0x01
    - 消息类型：0x02
    - 返回码：0x00

⌋

Magic Cookie 报文的布局如图 4.15 所示。

**客户端 → 服务器：**

**服务器 → 客户端：**

**图 4.15：SOME/IP 的 Magic Cookie 报文**

#### 4.2.1.3 多服务实例

[PRS_SOMEIP_00162] ⌈同一服务的服务实例通过不同的实例 ID 标识。应支持多个服务实例驻留在不同的 ECU 上，以及一个或多个服务的多个服务实例驻留在单个 ECU 上。⌋

[PRS_SOMEIP_00163] ⌈不同服务的多个服务实例应能够共享所使用的传输层协议的相同端口号，但单个 ECU 上同一服务的多个服务实例应每个服务实例使用不同的端口。⌋

**原理**：虽然实例 ID 用于服务发现，但它们不包含在 SOME/IP 头部中。

服务实例可以通过服务 ID 与套接字（即 IP 地址、传输协议（UDP/TCP）和端口号）的组合来识别。建议实例对 UDP 和 TCP 使用相同的端口号。如果一个服务实例使用 UDP 端口 x，则只有该服务实例而不是同一服务的另一个实例应该使用 TCP 端口 x 提供其服务。

#### 4.2.1.4 通过 UDP 传输大型 SOME/IP 报文（SOME/IP-TP）

SOME/IP 的 UDP 绑定只能传输直接适合 IP 数据包的 SOME/IP 报文。如果需要通过 UDP 传输更大的 SOME/IP 报文（例如 32 KB），则应使用 SOME/IP 传输协议（SOME/IP-TP）。太大而无法直接通过 UDP 绑定传输的 SOME/IP 报文称为"原始" SOME/IP 报文。在 SOME/IP-TP 报文中传输的原始 SOME/IP 报文负载的"片段"称为"分段"。

**仅当需要传输非常大的数据块（> 1400 字节）且在错误情况下没有严格的延迟要求时，才使用 TCP**

[PRS_SOMEIP_00720] ⌈使用 SOME/IP-TP 的 SOME/IP 报文应激活会话处理（会话 ID 必须对原始报文唯一）。⌋

[PRS_SOMEIP_00721] ⌈所有 SOME/IP-TP 分段都应携带原始报文的会话 ID；因此，它们都具有相同的会话 ID。⌋

[PRS_SOMEIP_00722] ⌈SOME/IP-TP 分段应将消息类型的 TP 标志设置为 1。⌋

[PRS_SOMEIP_00723] ⌈SOME/IP-TP 分段应在 SOME/IP 头部之后（即 SOME/IP 负载之前）有一个 TP 头部，其结构如下（位从最高到最低）：
- 偏移量 [28 位]
- 保留标志 [1 位]
- 保留标志 [1 位]
- 保留标志 [1 位]
- 更多分段标志 [1 位]

⌋

SOME/IP-TP 头部如图 4.16 所示。

**图 4.16：SOME/IP TP 头部**

[PRS_SOMEIP_00931] ⌈SOME/IP-TP 头部应采用网络字节序（大端）编码。⌋

[PRS_SOMEIP_00724] ⌈偏移量字段应传输 uint32 的高 28 位。低 4 位应始终解释为 0。⌋

**注意**：这意味着偏移量字段只能传输 16 字节倍数的偏移值。

[PRS_SOMEIP_00725] ⌈TP 头部的偏移量字段应设置为所传输分段在原始报文中的字节偏移量。⌋

[PRS_SOMEIP_00726] ⌈保留标志应由发送方设置为 0，并由接收方忽略（不检查）。⌋

[PRS_SOMEIP_00727] ⌈除最后一个分段外，所有分段的更多分段标志应设置为 1。对于最后一个分段，应设置为 0。⌋

[PRS_SOMEIP_00728] ⌈SOME/IP 长度字段应按之前的规定使用。这意味着它覆盖 SOME/IP 头部的前 8 个字节以及之后的所有字节。⌋

**注意**：这意味着对于传输分段的 SOME/IP-TP 报文，SOME/IP 长度覆盖 8 字节的 SOME/IP 头部、4 字节的 TP 头部以及分段本身。

[PRS_SOMEIP_00729] ⌈分段的长度必须反映基于偏移量字段的下一个分段的对齐。因此，除最后一个分段外，所有分段的长度都应是 16 字节的倍数。⌋

[PRS_SOMEIP_00730] ⌈由于基于 UDP 的 SOME/IP 报文限制为 1400 字节负载，因此正确对齐的分段的最大长度为 1392 字节。⌋

[PRS_SOMEIP_00731] ⌈SOME/IP-TP 报文应使用与原始报文相同的消息 ID（即服务 ID 和方法 ID）、请求 ID（即客户端 ID 和会话 ID）、协议版本、接口版本和返回码。⌋

**注意**：如上所述，长度、消息类型和负载由 SOME/IP-TP 调整。

**示例**

本示例描述了如何传输一个具有 5880 字节负载的原始 SOME/IP 报文。该原始 SOME/IP 报文的长度字段设置为 8 + 5880 字节。

**图 4.17：示例：原始 SOME/IP 报文的头部**

该原始 SOME/IP 报文现在将被分段为 5 个连续的 SOME/IP 分段。在本示例中，这些分段的每个负载最多携带 1392 字节。

对于这些分段，SOME/IP TP 模块添加了额外的 TP 字段（标记为红色）。SOME/IP 的长度字段携带 SOME/IP 分段的总长度，包括 8 字节的请求 ID、协议版本、接口版本、消息类型和返回码。由于添加了 4 字节的 TP 字段，此长度信息增加了 4 个额外的 SOME/IP TP 字节。

下图提供了每个 SOME/IP 分段的相关 SOME/IP 头部设置的概述：

||长度（字节）|消息类型 [TP 标志]|偏移量值|更多分段标志|
|---|---|---|---|---|
|第 1 个分段|8 + 4 + 1392 = 1404|TP 标志 = '1'|0|1|
|第 2 个分段|8 + 4 + 1392 = 1404|TP 标志 = '1'|87|1|
|第 3 个分段|8 + 4 + 1392 = 1404|TP 标志 = '1'|174|1|
|第 4 个分段|8 + 4 + 1392 = 1404|TP 标志 = '1'|261|1|
|第 5 个分段|8 + 4 + 312 = 324|TP 标志 = '1'|348|0|

**图 4.18：示例：相关 SOME/IP TP 头部概述**

**注意**：请注意，偏移量字段中提供的值以 16 字节为单位，即：偏移量值 87 对应 1392 字节负载。

SOME/IP 分段报文的完整 SOME/IP 头部详细信息如下：

前 4 个分段每个包含 1392 字节负载，"更多分段标志"设置为 '1'：

**图 4.19：示例：SOME/IP 分段的头部**

- 最后一个分段（即 #5）包含原始 5880 字节负载的剩余 312 字节。此最后一个分段的"更多分段标志"设置为 '0'。

**图 4.20：示例：最后一个 SOME/IP 分段的头部**

**发送方特定行为**

[PRS_SOMEIP_00732] ⌈发送方应仅对配置为分段的报文进行分段。⌋

[PRS_SOMEIP_00733] ⌈发送方应按升序发送分段。⌋

[PRS_SOMEIP_00734] ⌈发送方应以这样的方式进行分段：所有更多分段标志设置为 1 的分段都具有相同的大小。⌋

[PRS_SOMEIP_00735] ⌈发送方应在本规范施加的限制内尝试最大化分段的大小。⌋

[PRS_SOMEIP_00736] ⌈发送方不应发送重叠或重复的分段。⌋

**接收方特定行为**

[PRS_SOMEIP_00738] ⌈接收方应基于消息 ID、协议版本、接口版本和消息类型（不带 TP 标志）的配置值匹配分段以进行重组。⌋

[PRS_SOMEIP_00740] ⌈应支持并行重组来自不同客户端（发送方 IP、发送方端口或客户端 ID 不同）的具有相同消息 ID 的多个报文。这应由配置控制，并确定"重组缓冲区"的数量。⌋

[PRS_SOMEIP_00741] ⌈会话 ID 应用于检测要重组的下一个原始报文。⌋

[PRS_SOMEIP_00742] ⌈如果接收到具有不同会话 ID 的新分段，接收方应开始新的重组（并可能丢弃未成功重组的旧分段）。⌋

[PRS_SOMEIP_00743] ⌈接收方应仅重组到其配置的缓冲区大小，并跳过报文的其余部分。⌋

[PRS_SOMEIP_00744] ⌈只有正确重组的、大小不超过配置大小的报文才应传递给应用程序。⌋

**注意**：这意味着实现必须确保报文的所有字节都是已正确接收和重组的字节。计算非重叠、非重复的字节数并将其与长度进行比较可能是一种有效的检查方法。

[PRS_SOMEIP_00745] ⌈重组后的报文应使用用于重组的最后一个分段的返回码。⌋

[PRS_SOMEIP_00746] ⌈在将 SOME/IP TP 分段重组为大型未分段报文的过程中，应调整消息类型，将 TP 标志重置为 0。⌋

[PRS_SOMEIP_00747] ⌈接收方应支持重组按升序和降序接收的分段。⌋

[PRS_SOMEIP_00749] ⌈当在 SOME/IP 报文的组装过程中检测到缺失分段时，应取消当前组装过程。⌋

**注意**：这意味着不支持重排序。

[PRS_SOMEIP_00750] ⌈不支持使用同一缓冲区交错不同的分段报文（例如，仅会话 ID 和负载不同）。⌋

**注意**：这禁止了当某些分段被重排序时，相同事件（相同消息 ID、IP 地址、端口号和传输协议）以错误顺序到达的情况。

[PRS_SOMEIP_00751] ⌈完全不同的原始报文（例如消息 ID 不同）的分段重排序无关紧要，因为这些分段会进入不同的缓冲区。⌋

[PRS_SOMEIP_00752] ⌈接收方应通过基于最后接收的分段覆盖来正确重组重叠和重复的分段。⌋

[PRS_SOMEIP_00753] ⌈如果重叠或重复的分段更改了缓冲区中已写入的字节，接收方可以取消重组，前提是此功能可以通过配置关闭。⌋

[PRS_SOMEIP_00754] ⌈接收方应能够优雅地检测和处理明显的错误。例如，如果 MS=1 的分段长度不是 16 的倍数，则取消重组。⌋

**注意**：这意味着接收代码应防止缓冲区溢出或其他故障。

### 4.2.2 请求/响应通信

最常见的通信模式之一是请求/响应模式。一个通信伙伴（客户端）发送请求报文，由另一个通信伙伴（服务器）应答。

[PRS_SOMEIP_00920] ⌈对于 SOME/IP 请求报文，客户端必须对负载和头部执行以下操作：
- 构造负载
- 根据客户端想要调用的方法设置消息 ID
- 将长度字段设置为 8 字节（用于 SOME/IP 头部中长度字段之后的部分）+ 序列化负载的长度
- 可选地将请求 ID 设置为唯一编号（仅需对客户端唯一）
- 根据 [PRS_SOMEIP_00052] 设置协议版本
- 根据接口定义设置接口版本
- 将消息类型设置为 REQUEST（即 0x00）
- 将返回码设置为 0x00

⌋

[PRS_SOMEIP_00921] ⌈为了构造请求报文的负载，方法的所有输入或输入输出参数应按照方法签名中参数的顺序进行序列化。⌋

[PRS_SOMEIP_00922] ⌈服务器基于客户端请求的头部构建响应的头部，并额外执行以下操作：
- 构造负载
- 从相应请求中接管消息 ID
- 将长度设置为 8 字节 + 新负载大小
- 从相应请求中接管请求 ID
- 将消息类型设置为 RESPONSE（即 0x80）或 ERROR（即 0x81）
- 将返回码设置为被调用方法的返回码，或者在错误报文的情况下设置为有效的错误码

⌋

有关有效的返回码，参见表 4.12。

[PRS_SOMEIP_00923] ⌈为了构造响应报文的负载，方法的所有输出或输入输出参数应按照方法签名中参数的顺序进行序列化。⌋

[PRS_SOMEIP_00927] ⌈在接收到相应的请求报文之前，服务器不应发送具有特定请求 ID 的响应报文。⌋

[PRS_SOMEIP_00928] ⌈当相应的请求报文尚未完全发送时，客户端应忽略具有特定请求 ID 的响应报文的接收。⌋

### 4.2.3 发后即忘通信

没有响应报文的请求称为发后即忘。

[PRS_SOMEIP_00924] ⌈对于 SOME/IP 发后即忘请求报文，客户端必须对负载和头部执行以下操作：

- 构造负载

- 根据客户端想要调用的方法设置消息 ID

- 将长度字段设置为 8 字节（用于 SOME/IP 头部中长度字段之后的部分）+ 序列化负载的长度

- 可选地将请求 ID 设置为唯一编号（仅需对客户端唯一）

- 根据 [PRS_SOMEIP_00052] 设置协议版本

- 根据接口定义设置接口版本

- 将消息类型设置为 REQUEST_NO_RETURN（即 0x01）

- 将返回码设置为 0x00

⌋

[PRS_SOMEIP_00171] ⌈发后即忘报文不应返回错误。错误处理和返回码应在需要时由应用程序实现。⌋

### 4.2.4 通知事件

通知描述了通用的发布/订阅概念。通常，服务器发布一个服务，客户端订阅该服务。在某些情况下，服务器会向客户端发送事件，例如更新的值或发生的事件。

SOME/IP 仅用于传输更新的值，而不用于发布和订阅机制。这些机制由 SOME/IP-SD 实现。

[PRS_SOMEIP_00925] ⌈对于 SOME/IP 通知报文，服务器必须对负载和头部执行以下操作：
- 构造负载
- 根据服务器想要发送的事件设置消息 ID
- 将长度字段设置为 8 字节（用于 SOME/IP 头部中长度字段之后的部分）+ 序列化负载的长度
- 将客户端 ID 设置为 0x00
- 根据 [PRS_SOMEIP_00932]、[PRS_SOMEIP_00933] 和 [PRS_SOMEIP_00521] 设置会话 ID。在会话处理激活的情况下，每次传输时会话 ID 应递增
- 根据 [PRS_SOMEIP_00052] 设置协议版本
- 根据接口定义设置接口版本
- 将消息类型设置为 NOTIFICATION（即 0x02）
- 将返回码设置为 0x00

⌋

[PRS_SOMEIP_00926] ⌈通知报文的负载应包含事件的序列化数据。⌋

[PRS_SOMEIP_00930] ⌈当同一 ECU 上存在多个订阅客户端时，系统应处理通知的复制，以节省通信介质上的传输。⌋

当使用多播报文传输通知时，这一点尤为重要。

#### 4.2.4.1 发送通知的策略

对于不同的用例，可以使用不同的发送通知策略。以下是常见的示例：
- **周期性更新**：以固定间隔发送更新值（例如，对于带有存活信号的安全相关报文，每 100 毫秒一次）
- **变化时更新**：一旦"值"发生变化就发送更新（例如，车门打开）
- **增量变化**：仅当与上次值的差异大于某个增量时才发送更新。此概念可以是自适应的，即预测基于历史；因此，仅当预测值与当前值的差异大于增量时才传输更新。

### 4.2.5 字段

字段表示一个状态并具有有效值。订阅字段的消费者在订阅后立即获得字段值作为初始事件。

[PRS_SOMEIP_00179] ⌈字段应是获取器、设置器和通知事件的组合。⌋

[PRS_SOMEIP_00180] ⌈不存在没有设置器、没有获取器且没有通知器的字段。字段应至少包含一个获取器、一个设置器或一个通知器。⌋

[PRS_SOMEIP_00181] ⌈字段的获取器应是一个请求/响应调用，其请求报文中的负载为空，响应报文中的负载为字段的值。⌋

[PRS_SOMEIP_00182] ⌈字段的设置器应是一个请求/响应调用，其请求报文中的负载为字段的期望值，响应报文中的负载为设置到字段的值。⌋

**注意**：如果请求负载的值被调整（例如因为它超出限制），则调整后的值将在响应负载中传输。

[PRS_SOMEIP_00909] ⌈当客户端订阅字段时，通知器应发送一个事件报文，将字段的值传输给客户端。⌋

[PRS_SOMEIP_00183] ⌈通知器应在值变化时发送传输字段值的事件报文，并遵循事件的规则。⌋

### 4.2.6 错误处理

错误处理可以在应用程序或下层通信层中完成。因此，SOME/IP 支持两种不同的机制：
- 方法响应报文中的返回码
- 显式错误报文

使用哪一种取决于配置。

[PRS_SOMEIP_00901] ⌈方法响应报文中的返回码应用于将应用程序错误和方法的响应数据从提供者传输给方法的调用者。⌋

**注意**：请注意，从 SOME/IP 的角度来看，请求和响应方法的返回码不被视为错误。这意味着如果请求/响应方法以不等于 0x00 的返回码退出（如果 AUTOSAR ClientServerOperation 的 ApplicationError 不同于 E_OK），消息类型仍然是 0x80。

[PRS_SOMEIP_00902] ⌈显式错误报文应用于将应用程序错误和响应数据或通用 SOME/IP 错误从提供者传输给方法的调用者。⌋

[PRS_SOMEIP_00903] ⌈如果需要传输更详细的错误信息，则错误报文（消息类型 0x81）的负载应填充特定于错误的数据，例如异常字符串。应发送错误报文而不是响应报文。⌋

这可用于处理服务器中可能发生的所有不同应用程序错误。此外，可能会出现通信介质或中间组件（例如交换机）的问题，这些问题必须通过例如可靠传输的方式来处理。

所有报文在其头部都有一个返回码字段。（参见第 4.1.2 节）

[PRS_SOMEIP_00904] ⌈只有响应（响应报文（消息类型 0x80）和错误报文（消息类型 0x81））应使用返回码字段向它们应答的请求（消息类型 0x00）携带返回码。⌋

[PRS_SOMEIP_00905] ⌈除 0x80 和 0x81 之外的所有其他报文应将此字段设置为 0x00。⌋

有关消息类型，参见第 4.1.2.8 节。

#### 4.2.6.1 返回码

[PRS_SOMEIP_00187] ⌈返回码应为 UINT8 类型。⌋

[PRS_SOMEIP_00191] ⌈当前定义的返回码如表 4.12 所示。⌋

|ID|名称|描述|
|---|---|---|
|0x00|E_OK|未发生错误|
|0x01|E_NOT_OK|发生未指定的错误|
|0x02|E_UNKNOWN_SERVICE|请求的服务 ID 未知|
|0x03|E_UNKNOWN_METHOD|请求的方法 ID 未知，服务 ID 已知|
|0x04|E_NOT_READY|服务 ID 和方法 ID 已知，应用程序未运行|
|0x05|E_NOT_REACHABLE|运行服务的系统不可达（仅内部错误码）|
|0x06|E_TIMEOUT|发生超时（仅内部错误码）|
|0x07|E_WRONG_PROTOCOL_VERSION|不支持的 SOME/IP 协议版本|
|0x08|E_WRONG_INTERFACE_VERSION|接口版本不匹配|
|0x09|E_MALFORMED_MESSAGE|反序列化错误，无法反序列化负载|
|0x0a|E_WRONG_MESSAGE_TYPE|接收到意外的消息类型（例如，对于定义为 REQUEST 的方法收到 REQUEST_NO_RETURN）|
|0x0b|E_E2E_REPEATED|重复的 E2E 计算错误|
|0x0c|E_E2E_WRONG_SEQUENCE|错误的 E2E 序列错误|
|0x0d|E_E2E|未进一步指定的 E2E 错误|
|0x0e|E_E2E_NOT_AVAILABLE|E2E 不可用|
|0x0f|E_E2E_NO_NEW_DATA|没有用于 E2E 计算的新数据|
|0x10 - 0x1f|RESERVED|保留用于通用 SOME/IP 错误，这些错误将在本文档的未来版本中指定|
|0x20 - 0x5E|RESERVED|保留用于服务和方法的特定错误，这些错误由接口规范指定|

**表 4.12：返回码**

返回码的生成和处理应是可配置的。

[PRS_SOMEIP_00539] ⌈SOME/IP 错误报文（即返回码 0x01 - 0x1f）不应被应答为错误报文。⌋

#### 4.2.6.2 错误报文

为了更灵活的错误处理，SOME/IP 允许使用特定于错误报文的不同报文布局，而不是使用响应报文的报文布局。

异常报文的推荐布局如下：
- 特定异常的联合体。至少需要存在一个没有字段的通用异常
- 用于异常描述的动态长度字符串

**原理**：联合体提供了以类型安全的方式在未来添加新异常的灵活性。字符串用于传输人类可读的异常描述，以简化测试和调试。

[PRS_SOMEIP_00188] ⌈SOME/IP 报文的接收方不应为事件/通知返回错误报文。⌋

[PRS_SOMEIP_00189] ⌈SOME/IP 报文的接收方不应为发后即忘方法返回错误报文。⌋

[PRS_SOMEIP_00537] ⌈如果消息类型被错误地设置为请求或响应，SOME/IP 报文的接收方不应为事件/通知和发后即忘方法返回错误报文。⌋

[PRS_SOMEIP_00190] ⌈对于请求/响应方法，错误报文应复制 SOME/IP 头部的字段（即消息 ID、请求 ID 和接口版本），但不复制负载。此外，必须将消息类型和返回码设置为适当的值。⌋

#### 4.2.6.3 错误处理概述

[PRS_SOMEIP_00576] ⌈错误处理应基于接收到的消息类型（例如，只有方法可以用返回码应答），并应按照 [PRS_SOMEIP_00195] 定义的顺序进行检查。⌋

[PRS_SOMEIP_00910] ⌈对于通过 UDP 接收的 SOME/IP 报文，应检查以下内容：
- UDP 数据报大小应至少为 16 字节（SOME/IP 报文的最小大小）
- 长度字段的值应小于或等于 UDP 数据报负载中的剩余字节数

如果任何一项检查失败，应发出格式错误错误。⌋

[PRS_SOMEIP_00195] ⌈SOME/IP 报文应通过错误处理进行检查。这不包括基于应用程序的错误处理，仅涵盖消息传递和 RPC 中的错误处理。⌋

错误处理的概述如图 4.21 所示。

**图 4.21：SOME/IP 中的报文验证和错误处理**

[PRS_SOMEIP_00614] ⌈当通过 TCP 接收 SOME/IP 报文时发生 [PRS_SOMEIP_00195] 中指定的错误之一时，接收方应检查 TCP 连接，并在需要时重新启动 TCP 连接。⌋

**原理**：

检查 TCP 连接可能包括以下内容：
- 检查是否接收到例如其他事件组的数据
- 发送 Magic Cookie 报文并等待 TCP ACK
- 重新建立 TCP 连接

#### 4.2.6.4 通信错误和通信错误处理

在考虑 RPC 报文的传输时，存在不同的可靠性语义：
- **可能（Maybe）**：报文可能到达通信伙伴
- **至少一次（At least once）**：报文至少到达通信伙伴一次
- **恰好一次（Exactly once）**：报文恰好到达通信伙伴一次

当使用上述术语时，对于请求/响应，该术语适用于两种报文（即请求和响应或错误）。

虽然不同的实现可能采用不同的方法，但 SOME/IP 目前在使用 UDP 绑定时实现"可能"可靠性，在使用 TCP 绑定时实现"恰好一次"可靠性。进一步的错误处理留给应用程序。

对于"可能"可靠性，当使用 UDP 作为传输协议结合请求/响应通信时，只需要一个超时。图 4.22 显示了"可能"可靠性的状态机。客户端的 SOME/IP 实现必须在指定的超时时间内等待响应。如果发生超时，SOME/IP 应向客户端应用程序发出 E_TIMEOUT 信号。

**图 4.22："可能"可靠性的状态机**

对于"恰好一次"可靠性，可以使用 TCP 绑定，因为 TCP 被定义为允许可靠通信。

## 4.3 接口版本的兼容性规则

接口版本标识负载格式。负载格式受以下因素影响：
- 服务接口规范
- 序列化配置（例如可变长度数组的使用、长度字段的大小、填充、TLV、SOME/IP-TP）
[PRS_SOMEIP_00937] ⌈由于以下任何原因，接口版本应递增：
- 负载格式的不兼容变更
- 服务行为的不兼容变更
- 应用程序设计要求

⌋

**注意**：负载格式的兼容变更不应递增接口版本。

[PRS_SOMEIP_00938] ⌈表 4.13 中的规则应定义负载格式变更的兼容性。对于复杂数据类型，这些规则应递归应用。X 表示兼容变更，空单元格表示不兼容变更。⌋

**注意**：此表基于 SOME/IP 协议的规范。作为经验法则，如果数据的接收方在预期位置找到所有预期信息，则接口是兼容的。

---

