SOME/IP 是"Scalable service-Oriented MiddlewarE over IP"的缩写,即可扩展的面向服务的IP中间件,由AUTOSAR发布。它是一种自动/嵌入式通信协议,它支持远程过程调用、事件通知和底层序列化/线格式。唯一有效的缩写是SOME/IP,不要写成Some/IP。
以下知识点可以先初步认识SOME/IP:
1.SOME/IP 位于OSI 7层模型的4层之上,当接收方有需求的时候才发送,可以大大降低总线的负载。
2.SOME/IP报文中“Message Type”有以下几种类型:
Notification属于通知类的服务,首先由Client向Server订阅服务内容,然后Server向Client自动发布服务内容。Notification分为Event和Field两类,两者都要先向Server订阅,区别在于Event是某一时刻的快照,只是事件的通知,而Field除了事件通知之外,还有Getter和Setter的功能,即对信息进行读写的操作。
3.SOME/IP-SD是SOME/IP的一种特殊服务,可以让Client知道Server可以提供哪些服务,some/ip有两种动态发现服务的机制:一种是Offer Service,由Server向网络上的小伙伴告知它所提供的服务;另一种是Find Service,由Client向Server请求可用的服务。
本文主要内容是SOME/IP 官方文档的翻译。原文地址:AUTOSAR_RS_SOMEIPProtocol.pdf
1.引言和概览
2. 协议要求
3. 缩略语和术语
术语/缩略语 | 描述 |
---|---|
Byte Order Mark | 字节顺序标记(byte order mark, BOM)是一个Unicode字符,U+FEFF字节顺序标记(byte order mark, BOM),它作为一个魔数出现在文本流的开头,用于表明所使用的编码 |
Method | 可被调用的方法、过程、函数或子程序 |
Parameters | 方法或事件的输入、输出或输入/输出参数 |
Remote Procedure Call (RPC) | 从一个ECU到另一个ECU的方法调用,使用消息传输 |
Request | 客户端发给服务端用来调用方法(Method)的消息 |
Response | 服务端响应客户端方法调用的消息 |
Request/Response communication | 一次请求/回复过程,也就是RPC |
Event(事件) | 单向数据传输,只在变化时调用或循环调用,从数据的生产者发送到消费者 |
Field | Field(字段)代表一种状态,因此在getter、setter和notifier操作的任何时候都有一个有效值 |
Notification Event | 用字段通知的事件消息 |
Getter | 允许读取字段的请求/响应调用。 |
Setter | 允许对字段进行写访问的请求/响应调用。 |
Service | 零个或多个方法、零个或多个事件、零个或多个字段的逻辑组合。 |
Eventgroup | 一个服务中事件和字段通知事件的逻辑组合,以便允许订阅 |
Service Instance | 一个服务的实现,它可以在车辆上多次存在,也可以在ECU上多次存在 |
Server | 提供服务实例的ECU在这个服务实例的上下文中应该被称为服务器。 |
Client | 在这个服务实例的上下文中,使用服务器服务实例的ECU应该被称为客户端。 |
Fire and Forget | 无回复的请求叫做 解雇并遗忘 |
User Datagram Protocol | 一种传输层协议 |
Union | 一种动态假设不同数据类型的数据结构 |
non-extensible (standard) struct | 序列化时不带标签的结构体。最多可以在结构体末尾以兼容的方式添加新成员,而不能添加可选成员。 |
extensible struct | 用标签序列化的结构体。可以在任意位置以兼容的方式添加新成员,也可以添加可选成员。 |
SOME/IP 是一种基于网络的服务导向协议。它基于列出服务提供的功能的服务定义。一个服务可以由零个或多个事件、方法和字段的组合组成。
事件(Events)指由提供者给订阅者,周期性或更改时发送的数据
方法(Methods)为订阅者提供了发出在提供者端执行远程过程调用的可能性
字段(Fields)由下面三种的一个或多个组合而成:
- 一个通知器(notifier),数据发生更改时,由提供者发给订阅者
- 一个getter,它可以被订阅者调用以显式地查询提供者的值
- 一个setter,订阅者在想要更改提供者端上的值时可以调用的setter
字段的通知器和事件的主要区别是事件只在发生变化时发送,字段的通知器在订阅后直接发送数据。
序列化描述的是数据以协议数据单元(pdu)的形式表示,作为UDP或TCP消息的有效载荷,在基于ip的车载网络上传输。
消息ID是一个32位的标识符,用于标识对应用程序方法的RPC调用或者标识一个事件。消息ID在配置的时候,要求具备唯一性。
方法ID是消息ID的一部分
Service ID [16 Bit] | 0 [1 Bit] | Method ID [last 15 Bit] |
---|
事件组是一个服务中事件和字段通知事件的逻辑组合,以便允许订阅。事件ID结构如下表所示:
Service ID [16 Bit] | 1 [1 Bit] | Event ID [last 15 Bit] |
---|
该字段表示从"Request ID"字段开始,到SOME/IP报文结束的字节长度。
请求ID允许提供者和订阅者区分同一方法、事件、读取方法或设置方法的多个并行使用。
请求ID的结果如下表所示
Client ID [16 Bit] | Session ID [last 15 Bit] |
---|
注意:这意味着ECU的实现者可以根据其实现的需要定义客户端id,而提供者不需要知道此布局或定义,因为他只需在响应中复制完整的请求id。
Client ID Prefix [8 Bits] | Client ID [8 Bits] | Session ID [16 Bits] |
---|
Protocol Version 固定为1
接口版本应该是一个8位字段,包含服务接口的主要版本。
“消息类型”字段用于区分不同类型的消息,应包含以下值
Number | Value | Description |
---|---|---|
0x00 | Request | A request expecting a response (even void) |
0x01 | REQUEST_NO_RETURN | A fire&forget request |
0x02 | NOTIFICATION | A request of a notification/event callback expecting no response |
0x80 | RESPONSE | The response message |
0x81 | ERROR | The response containing an error |
0x20 | TP_REQUEST | A TP request expecting a response (even void) |
0x21 | TP_REQUEST_NO_RETURN | A TP fire&forget request |
0x22 | TP_NOTIFICATION | A TP request of a notification/event callback expecting no response |
0xa0 | TP_RESPONSE | The TP response message |
0xa1 | TP_ERROR | The TP response containing an error |
消息类型的第三高位(=0x20)被称为TP-Flag,应该设置为1,表示当前的SOME/IP消息是一个段。消息类型的其他位按本节中指定的方式设置。
消息类型请求(0x00)的段具有消息类型(0x20),消息类型响应(0x80)的段具有消息类型(0xa0),等等。具体操作请参见(4.2.1.4章)
返回码应使用,表示请求是否已成功处理。为了简化标题布局,每条消息都传输字段返回码。特定消息类型允许返回的代码如下表所示
Message Type | Allowed Return Codes |
---|---|
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) |
RESPONSE | See Return Codes in [PRS_SOMEIP_00191] |
ERROR | See Return Codes in [PRS_SOMEIP_00191]. Shall not be 0x00 (E_OK) |
在有效载荷字段中携带参数。参数的序列化将在下一节中指定。
SOME/IP有效载荷字段的大小取决于所使用的传输协议。使用UDP, SOME/IP有效载荷应该在0到1400字节之间。为了允许协议栈的未来更改(例如更改为IPv6或添加安全手段),需要限制为1400字节。由于TCP支持有效载荷分段,因此自动支持更大的有效载荷。
有效负载可能由事件的数据元素或方法的参数组成。
序列化基于接口规范定义的参数列表。接口规范定义了PDU中所有数据结构的确切位置,必须考虑内存对齐。
Alignment用于对齐数据的起始位置,在数据之后插入填充元素,以确保对齐的数据从某些内存地址开始。
有些处理器架构可以更有效地访问数据(例如master),当它们的起始地址是某个数字的倍数时(例如32位的倍数)。
数据结构序列化章节涉及的内容比较复杂,个人认为刚开始接触SOME/IP,没必要花费太大精力在这一章节,先学会如何使用SOME/IP即可。
本章描述SOME/IP的远程过程调用(RPC)、事件通知和错误处理。
SOME/IP 目前支持UDP和TCP传输,这节将介绍TCP/UDP 绑定,而第六章将讨论如何选择传输协议。
如果一个服务端运行同一服务的不同实例,属于不同服务实例的消息将通过服务器端的传输协议端口映射到该服务实例。更多细节见4.2.1.3章节。
所有传输协议绑定都应支持在传输层PDU(即UDP包或TCP段)中传输多个SOME/IP消息。
接收SOME/IP的实现方法(implementation)应能够接收由UDP或TCP传输的未对齐SOME/IP消息
理由是:
当在UDP或TCP中传输多个SOME/IP有效载荷时,只有当每个有效载荷的长度是对齐大小的倍数(例如32位)时,才能保证有效载荷的对齐。
首部格式允许在一个数据包中传输多个SOME/IP消息。SOME/IP实现通过SOME/IP长度字段来标识SOME/IP消息的结束。根据数据包长度字段,SOME/IP将确定数据包中是否有额外的SOME/IP消息。这适用于UDP和TCP传输。
每个SOME/IP载荷必须有自己的SOME/IP 头
一个服务实例可以使用以下设置来通信它的所有方法、事件和通知:
UDP 绑定需要使用UDP传输协议数据包实现,SOME/IP协议不能限制UPD分片的使用。
对于配置为使用UDP单播通信的服务实例的所有方法、事件和通知,客户端和服务器应使用一个UDP单播连接。
对于配置为使用UDP多播通信的服务实例的所有方法、事件和通知,客户端和服务器应使用一个UDP多播连接。
SOME/IP的TCP绑定很大程度上是基于UDP绑定的。与UDP绑定相比,TCP绑定允许更大的SOME/IP消息,并利用了TCP的健壮性特性(处理丢失、重排序、重复等)。为了降低延迟和反应时间,应关闭内格尔算法(TCP_NODELAY)。
当TCP链接丢失时,未建立的请求应按超时处理。由于TCP处理可靠性,SOME/IP不需要额外的可靠性手段。
对于配置为使用TCP通信的服务实例的所有方法、事件和通知,客户端和服务器应该使用一个TCP连接。
当传输第一个方法调用或客户端试图接收第一个通知时,TCP连接应由客户端打开。
当TCP链接失效时,由客户端负责重新建立TCP链接;当TCP链接不再被请求时,应有客户端关闭TCP链接;当使用TCP连接的所有服务不再可用时(停止或超时),客户端应关闭TCP连接。
服务器在停止所有服务时,不应停止TCP连接。给客户端足够的时间来处理控制数据,从而关闭TCP连接。因为当服务器在客户端意识到不再需要TCP之前关闭TCP连接时,客户端将尝试重新建立TCP连接。
为了允许测试工具识别通过TCP传输的SOME/IP消息的边界,可以在TCP消息流上以固定距离将SOME/IP魔法缓存消息插入SOME/IP消息中。
魔法缓存消息应该具备以下字段:
客户端 -> 服务端:
服务端 -> 客户端;
同一个服务的服务实例通过不同的实例id识别。应支持多个服务实例驻留在不同的ECU上,以及一个或多个服务的多个服务实例驻留在单个ECU上。
虽然不同服务的多个服务实例应该能够共享使用的传输层协议的相同端口,但在单个ECU上的同一个服务的多个服务实例应该对每个服务实例使用不同的端口–因为虽然实例ID用于SOME/IP SD,但它们不包含在SOME/IP 头中
服务实例可以通过服务ID和套接字(即ip地址、传输协议(UDP/TCP)和端口号)的组合来识别。建议UDP和TCP实例使用相同的端口号。如果一个服务实例使用UDP端口x,则只有该服务的实例,而同一服务的另一个实例不应该完全使用TCP端口x。
SOME/IP-TP是指SOME/IP Transport Protocol。当SOME/IP消息很大(大于32KB),我们需要SOME/IP-TP进行传输。SOME/IP消息太大,不能直接通过UDP绑定传输,称为“原始”SOME/IP消息。在SOME/IP- tp消息中传输的原始SOME/IP消息负载的“片段”称为“段”。
只有在需要传输非常大的数据块时才使用TCP (>1400字节),并且在存在错误的情况下没有硬延迟要求
这节内容使用不多,我们先跳过
SOME/IP 使用的R&R 通讯模型是最常用的通讯模型
SOME/IP 请求消息需要包含有效载荷和头,因此客户端需要做以下工作:
服务端回复报文的头部基于客户端请求报文的头部,它需要做到以下工作;
客户端也可以发送一种不需要回复的请求,这种报文的主要结构有:
因为客户端和服务端是订阅和发布的关系,所以通知是指服务端向客户端发布一项服务。客户端订阅服务后,当该服务出现了值更新或者一个事件发生,服务端就会向客户端发送一个报文。
SOME/IP仅仅用来传输报文,具体的订阅/发布机制,需要参考SOME/IP-SD协议
发送“通知(Notification)”类消息,服务端需要做以下工作:
当同一ECU上存在多个订阅客户端时,系统应处理通知的复制,以便在通信介质上保存传输。特别是使用多播消息传输通知时,这一点尤其重要。
一个场代表一种状态,它有一个有效值。在订阅后立即订阅该字段的消费者将该字段值作为初始事件获取。(The consumers subscribing for the field instantly after subscription get the field value as an initial event)。
SOME/IP 支持两种不同的错误处理机制:
基于你的配置信息,来决定使用哪种机制
返回码字段必须是UINT8
SOME/IP协议中定义的返回码如下表所示;
ID | Name | Description |
---|---|---|
0x00 | E_OK | No error occurred |
0x01 | E_NOT_OK | An unspecified error occurred |
0x02 | E_UNKNOWN_SERVICE | The requested Service ID is unknown. |
0x03 | E_UNKNOWN_METHOD | The requested Method ID is unknown. Service ID is unknown. |
0x04 | E_NOT_READY | Service ID and Method ID are known. Application not running. |
0x05 | E_NOT_REACHABLE | System running the service is not reachable (internal error code only). |
0x06 | E_TIMEOUT | A timeout occurred (internal error code only). |
0x07 | E_WRONG_PROTOCOL_VERSION | Version of SOME/IP protocol not supported |
0x08 | E_WRONG_INTERFACE_VERSION | Interface version mismatch |
0x09 | E_MALFORMED_MESSAGE | Deserialization error, so that payload cannot be deserialized. |
0x0a | E_WRONG_MESSAGE_TYPE | An unexpected message type was received (e.g.REQUEST_NO_RETURN for a method defined as REQUEST). |
0x0b | E_E2E_REPEATED | Repeated E2E calculation error |
0x0c | E_E2E_WRONG_SEQUENCE | Wrong E2E sequence error |
0x0d | E_E2E | Not further specified E2E error |
0x0e | E_E2E_NOT_AVAILABLE | E2E not available |
0x0f | E_E2E_NO_NEW_DATA | No new data for E2E calculation present. |
0x10 -0x1f | RESERVED | Reserved for generic SOME/IP errors. These errors will be specified in future versions of this document. |
0x20 -0x5E | RESERVED | Reserved for specific errors of services and methods. These errors are specified by the interface specification. |
为了更灵活地处理错误,SOME/IP允许针对错误消息使用不同的消息布局,而不是使用响应消息的消息布局。异常消息的布局推荐如下:
集合(Union)提供了在将来以类型安全的方式添加新异常的灵活性。该字符串用于传输人类可读的异常描述,以简化测试和调试。
事件/通知报文的接收方不能返回异常消息
Fire&forget报文的接收方不能返回异常消息
对于请求/响应方法,异常消息将复制SOME/IP报头的字段(即消息ID、请求ID和接口版本),但不复制有效载荷。此外,消息类型和返回代码必须设置为适当的值。
异常处理流程概览如下图所以:
SOME/IP消息应通过错误处理进行检查。这不包括基于应用程序的错误处理,而只包括消息传递和RPC中的错误处理。
在考虑RPC消息的传输时,存在不同的可靠性语义:
虽然不同的实现可能实现不同的方法,但SOME/IP目前在使用UDP绑定时实现“可能”可靠性,而在使用TCP绑定时实现“恰好一次”可靠性。进一步的错误处理留给应用程序。对于“可能”的可靠性,当使用请求/响应通信结合UDP作为传输协议时,只需要一个超时。下图显示了“可能”可靠性的状态机。客户端的SOME/IP实现必须等待指定超时的响应。如果超时发生,某些/IP将向客户端应用程序发送E_TIMEOUT信号。
接口版本标识有效载荷的格式。
有效载荷格式受以下因素影响:服务接口规范、序列化配置(例如:可变大小数组的使用、长度字段的大小、填充、TLV、SOME/IP-TP)
不在本文档介绍
SOME/IP支持UDP (User Datagram Protocol)和TCP (Transmission Control Protocol)协议。UDP是一个非常精简的传输协议,只支持最重要的功能(多路复用和使用校验和的错误检测),而TCP为实现可靠的通信增加了额外的功能。TCP不仅可以处理比特错误,还可以处理分段、丢失、重复、重新排序和网络拥塞。在车辆内部,许多应用程序需要非常短的超时才能快速做出反应。使用UDP可以更好地满足这些需求,因为应用程序本身可以处理不太可能发生的错误事件。例如,在循环数据的用例中,等待下一个数据传输而不是尝试修复最后一个数据传输通常是最好的方法。UDP的主要缺点是它不处理分段。因此,只能传输较小的数据块。
SOME/IP应该允许隧道(tunnel)CAN和FlexRay帧。然而,Message ID空间需要在两个用例之间进行协调。完整的SOME/IP报头应用于传输/隧道CAN/FlexRay
如果需要填充,接口设计者应在数据类型的定义中插入保留/填充元素(如果需要对齐),因为SOME/IP实现不会自动添加这种填充
服务端可以限制来自客户端的链接。服务器可以强制执行通信策略,以保护服务器免受恶意或未经授权的客户端攻击。也就是说,服务器可能会拒绝订阅事件组,或者拒绝未授权的方法调用。
客户端也可以限制来自服务端的链接。客户端可以执行通信策略来保护客户端免受恶意服务器的攻击。即客户端可以拒绝与未经授权的服务器通信。