详解SOME/IP协议文档-2 - 知乎“ 以下内容来源于AutoSar官网的AUTOSAR_PRS_SOMEIPProtocol文档 ”SOME/IP Payload“ SOME/IP Payload由事件的数据元素或方法的参数组成,大小取决于所使用的传输层协议,对于UDP,payload介于0到1400个字节之间…https://zhuanlan.zhihu.com/p/404570767
SOME/IP-TP 下面链接SOME/IP 传输协议 - 知乎对于不能装入一个UDP数据包的SOME/IP数据包,SOME/IP 传输协议的任务是对这些SOME/IP数据包分段,在接收端,再重新组装收到的SOME/IP段。 SOME/IP Transport Protocol是一个简单的协议来分割数据包,不实现重试机…https://zhuanlan.zhihu.com/p/58445297
SOME/IP,全称Scalable service-Oriented MiddlewarE over IP,基于IP的可扩展的面向服务的中间件
所以SOME/IP是一套用TCP/IP协议帮助不同平台上的分布式应用软件互相传递信息的机制
说白了,就是把服务接口里的内容通过SOME/IP这一套规则打包,交给TCP/IP传输
服务可以是多个事件、方法和字段的组合
单向数据传输,仅在更改或循环时调用,从数据的提供者发送到订阅者
事件提供了从提供者到订阅者的循环发送或变化的数据
由此可见,事件是订阅者对提供者提出订阅后,提供者把订阅的数据通过循环发送或变化时发送这两种方式,发送给订阅者
被调用的方法、函数或子例程。方法为订阅者提供了在提供者端执行远程过程调用的可能性
订阅者如果想让提供者执行某个函数或方法,就可以SOME/IP中的方法实现
一个字段代表一个状态,getter、setter和notifier可以对其进行操作
订阅者可以调用getter来获取提供者的值/状态
订阅者可以调用setter来更改提供者端的值/状态
订阅者可以调用notifier让提供者把变化的数据发送给订阅者
Event和Field notifier的区别,notifier在订阅后会发送一个当前值
SOME/IP是建立在TCP/IP协议上的应用层协议机制
,对接上面的应用软件的服务接口,它有自己的协议首部
4个字节,用来识别应用程序
的方法或事件
所以Message ID由Service ID(16 bit)和Method ID(15 bit)组成,前者表示应用,后者表示方法或事件
“ Eventgroup,服务内部字段的事件和通知事件的 逻辑分组,以允许订阅
举例来说,当你想用SOME/IP来远程控制汽车娱乐音响系统里的音乐播放器播放下一首歌时,音乐播放器就是用Service ID表示,而切换到下一曲的动作就是Method ID
由此看出 Message ID对于整个系统来说是唯一的
4个字节 ,它是从Request ID到Payload的字节长度
注意,不包括Message ID和Length本身
4个字节,用来区分订阅者和提供者之间的同一个方法、事件、getter或setter的多个并行使用
什么意思
当你前后两次远程控制音乐播放器切换到下一曲时,两次的Request ID是不同的,需要加以区分
准确来说,其实是Request ID里的Session ID来区分
Request ID由Client ID(2个字节)和Session ID(2个字节)组成
2个字节,Client ID是订阅者的的唯一标识,提供者用Client ID区分调用同一方法的多个订阅者
比如汽车方向盘控件可以控制娱乐音响系统的音乐播放器切换到下一曲,后排的控制面板也可以控制它,那么就可以用不同的Client ID区分它们
所以,每个Client ID在整个车辆中应该是唯一的
2个字节,用来区分同一发送者的连续请求或消息
既然Client ID是车辆中唯一的,而每次请求的Session ID又不能相同,所以Request ID对于提供者来说是唯一的
提供者在生产响应消息时,应该把Request ID复制到响应消息中,这样订阅者收到后可以根据Request ID判断这是哪条请求的响应
在响应到达之前,订阅者不得重用Request ID
由于提供者只需要在响应中复制Request ID,所以它并不需要提前知道Client ID
对于Session ID,还有一些注意点
“ 如果会话处理未激活,Session ID应该设置为0x0000
”
这里我的理解是,比如按方向盘上的切换到下一曲的按键,如果对于方向盘来说,这个动作如果由于故障等原因未生效,那么这个Session ID就设置为0x00
“ 如果会话处理处于激活状态,Session ID应设置为0x0001-0xFFFF范围内的值
”
“ 如果会话处理处于激活状态,Session ID应根据各自的用例递增
”
这里我的理解是,比如按方向盘上的切曲按键,每按一次,Session ID就加1
“ 当Session ID达到 最大值0xFFF
F时,它应该从0x0001重新开始
”
从这里能看出,如果Session ID为0x0000时,提供者并不会对这个请求作出反应
“ 如果响应的Session ID与请求的Session ID不匹配,订阅者必须忽略这个响应
”
1个字节,用来表示SOME/IP首部的格式
对于SOME/IP协议首部中的改动,Protocol Version应该增加,但是对于payload中格式的改变,Protocol Version不应该增加
1个字节,用来表示服务接口版本
1个字节,用于区分不同类型的消息
当没有错误发生时,常规请求(Message Type = 0x00)应由响应(Message Type = 0x80)回复,如果发生错误,则应回复错误消息(Message Type = 0x81)
也可以发送没有响应消息的请求(Message Type = 0x01)
对于TP消息来说,Message Type的第三个高位要设置为1,以表示这个SOME/IP是一个segment,所以0x00对应0x20
这里我不理解segment是什么意思
1个字节,用来表示请求是否已成功处理
SOME/IP的首部布局是请求和响应通用的,这表示请求消息也有Return Code,只是它的值是0x00
下图是响应消息的Return Code
“ SOME/IP Payload由事件的数据元素或方法的参数组成,大小取决于所使用的传输层协议,对于UDP,payload介于0到1400个字节之间,而由于TCP支持payload分段,所以支持更大的长度
”
SOME/IP payload应以网络字节顺序编码,也就是大端规则传输
“ 将结构化的数据按照一定规则转换成byte字节流,然后封装到SOME/IP的payload里,发送到网络上,这就是数据结构的 序列化
结构化的数据是并行的,而payload数据是串行的。数据结构的序列化就是把并行的结构化数据序列化成串行数据。更进一步地说,结构化数据里的元素的数据类型有可能是字符串,数字,布尔值等等,而payload数据只可能是byte数组。
那么怎样才能把包含不同数据类型的结构化数据序列化成byte数组呢,不同的项目有不同的规则
之前遇到的项目,是把结构化数据按照ASN.1的格式进行定义的,那么序列化时也是按ASN.1的规则编码成byte数组
Payload数据序列化基于接口规范定义的参数列表,接口规范定义了PDU中所有数据结构的确切位置,并且必须考虑内存对齐
所以SOME/IP payload数据一要根据项目规范,二还要考虑内存对齐
什么是内存对齐,之前在学习capl中的结构体时涉及过
计算机系统为了简化处理器与内存之间的传输,以及提升读取数据的速度,对数据在内存中的存储的位置进行了限制,要求是某个数k的倍数,这就是所谓的内存对齐
结构体中的元素由于大小不一,就需要设置这个k值,每个元素的长度必须是这个k值的倍数,如果不满足,就需要填充一定的内存空间以满足k值的倍数
设置对齐方式,并不意味着结构体的长度/大小发生了变化
而对于payload里的数据结构,如果某个元素大小可变,且不是序列化数据流中的最后一个元素,则应通过在可变元素数据后插入填充位来实现数据对齐
“ 固定长度数据元素后不应该有填充,如果非要填充,必须在规范中明确
“ 可变长度元素后面的数据对齐应为1、2、4、8、16、32个byte
结构体应按顺序序列化,还需考虑内存对齐
“ SOME/IP不能自动插入虚拟或填充数据。 根据配置情况,可以在结构体前插入长度字段, 以表示这个结构体数据在SOME/IP传输时的长度,长度字段的大小为8、16或32位
如果长度字段大小大于结构体长度,则仅反序列化指定的字节,并根据长度字段跳过其他字节,如果长度字段大小小于结构体长度,且接收方无法在本地提供数据来替换,则中止反序列化,并将SOME/IP消息视为格式错误
反序列化是对序列化的反向操作,接收方根据收到的SOME/IP payload进行的解析
“ 为了兼容之前或之后的版本,可以为结构成员或方法参数添加数据ID,有了这个ID,接收方才会反序列化,这样就可以设置可选成功,同时可以在任意位置添加新的成员
“ 每个数据ID在结构中是唯一的,不能重复,在不同的结构或方法中不需要唯一
“ 结构中同一层级的成员,要么全部定义数据ID,要么全部都不定义,方法中所有参数要么全部定义数据ID,要么全部都不定义
通过上图,可以看到结构体中一个元素的组成
第一个byte的6-4位,它的值表示后面数据的类型
“ Wire Type 4时长度字段是静态配置的,而5、6、7是忽略静态配置,忽略长度字段的大小,根据Wire Type选择长度字段的大小,5是1个byte,6是2个byte,7是4个byte,可以看上图
”
第一个byte的3-0位,和第二个byte的8位,共12位
如果结构的元素或方法的参数配置了Data ID,则应该在序列化字节流中插入tag,也就是下面这个东西
还有Strings、Arrays等数据类型
SOME/IP支持TCP和UDP传输消息,选择哪种传输协议,后续会讲到
如果服务器和多个客户端进行同一个服务的SOME/IP通信,它会运行同一服务的不同实例,这些实例的消息是通过传输层协议端口映射到该实例上的
也就是说为了辨别不同的实例,就需要用实例的消息的传输层端口来区分
传输层payload里允许有多个SOME/IP消息,根据长度字段确定每个SOME/IP消息
每个SOME/IP payload都应该有自己的SOME/IP首部
一个服务实例可以用以下方式来进行所有方法、事件和通知的通信
SOME/IP的UDP绑定通过UDP包传输SOME/IP消息来实现
“ SOME/IP协议不应限制UDP分片的使用
”
UDP payload里是SOME/IP消息,如果payload太大,在网络层会进行分片,而SOME/IP协议不能限制它的分片功能
“ 对于配置为使用UDP单播通信的服务实例的所有方法、事件和通知,客户端和服务器应使用单个UDP单播连接
”
这句话的意思是一个单播UDP能实现所有配置为UDP单播通信的方法、事件和通知
“ 一个多播UDP能实现所有配置为UDP多播通信的事件和通知
”
SOME/IP的TCP绑定通过TCP报文传输SOME/IP消息来实现
“ 一个单播TCP能实现所有配置为TCP单播通信的方法、事件和通知
”
这里有几个注意点
上面讲的TCP连接与断开的逻辑只针对SOME/IP的TCP协议而言,并不是标准TCP协议
的规范
记得在前面看到SOME/IP-TP
时并不了解其含义,这里就涉及到了
“ 通过UDP传输长度很长的SOME/IP消息
”
SOME/IP的UDP绑定只能传输直接适合IP数据包
的SOME/IP消息
什么意思
“ 就是SOME/IP消息的大小最好能在IP层不需要分片直接传输
”
如果需要通过UDP传输更大的SOME/IP消息,则应使用SOME/IP-TP
“ 就是说如果SOME/IP消息太大,就要用SOME/IP-TP协议, 相当于在SOME/IP层对SOME/IP消息进行分片,这时候的SOME/IP Header就变成了SOME/IP-TP Header
”
SOME/IP消息太大而不能用UDP绑定直接传输的,称为“原始”SOME/IP消息
在SOME/IP-TP消息中传输的原始SOME/IP消息payload的“片段”称为segments
“ 所有的SOME/IP-TP段必须携带原始消息的Session ID,因此,它们都具有相同的 Session-ID
”
“ SOME/IP-TP segments应将消息类型的TP-Flag设置为1
”
就是其中有一位要设置为1,可以回顾下前面的文章
举例
原始SOME/IP消息为
如何分片
SOME/IP-TP消息
最后一条SOME/IP-TP消息
可以看出,SOME/IP-TP其实是对SOME/IP长消息的分片,SOME/IP-TP的首部相比较SOME/IP的首部,有两个不同点
“ 发送方应仅对配置为分段的消息进行分段,且按顺序发送
”
“ 发送方应把More Segment Flag设置为1的所有段都分片成相同的大小,也就是说除了最后一片,其他的分片大小都要相同,都是1392个byte
”
“ 发送方不得发送重复的分片报文
”
Protocol-Version、Interface-Version和Message-Type
(w/o TP Flag)进行重组
Session ID用于检测下一个要重组的原始消息
这句话说明了什么
说明同一原始SOME/IP消息分片的Segment的Session ID相同,同时它还是接收方重组新的分片的标志
如果接收到具有不同Session-ID的Segment,则接收方应开始新的重组(并可能丢弃未成功重组的旧段)
这句话说明如果收到新的Session ID,而之前的重组还未完成,接收方需要开始新的重组,这时候就会导致之前的还未完成的重组的消息被丢弃
这种情况通常发生在新的消息的分片跑到了旧消息的分片的前面,被接收方收到
只有正确重组的消息才能传递给 应用程序
消息的每个分片都有Return Code,只有最后一片的Return Code在重组时被使用, 当检测到有丢失的分片时,应该取消之前的重组,这意味着不支持重新排序, 接收方还支持覆盖重复的分片以正确重组消息。
请求/响应模式
是最常见的通信模式之一,一个通信伙伴(客户端)发送请求消息,由另一个通信伙伴(服务器)应答
客户端发送的SOME/IP Request消息的Header和Payload必须符合如下要求:
服务器发送的SOME/IP Response消息的Header和Payload必须符合如下要求:
“ 服务器/客户端在接收到完整的请求/响应消息前,忽略新的请求/响应报文
”
没有响应消息的请求称为fire&forget
客户端发送的SOME/IP request-no-return消息的Header和Payload必须符合如下要求:
“ Fire&Forget消息调用方法成功时服务器不会回复响应,发生错误时也不会返回错误,错误处理和返回码应在需要时由 应用程序
实现
”
通知事件描述了一般的发布/订阅概念,通常服务器发布客户端订阅的服务
在某些情况下,服务器会向客户端发送一个事件,例如更新的值或发生的事件
SOME/IP仅用于传输更新的值,而不用于发布和订阅机制,这些机制由SOME/IP-SD实现
也就是说,客户端发送SOME/IP-SD类型的消息来订阅服务,而服务器通过Notification消息发布客户端订阅的服务
服务器发送的SOME/IP Notification消息的Header和Payload必须符合如下要求:
对于不同的用例,发送通知消息的策略可能不同,以下是常见的示例:
时更新:仅当与最后一个值的差异大于某个值时才发送更新,这个概念可能是自适应的,即预测是基于历史的,因此,只有当预测值
一个字段表示一个状态,并具有有效值,订阅该字段的订阅者将字段值作为初始事件
“ 字段是getter、setter和notification event的集合
”
“ 没有setter、getter和notifier的字段不应该存在,该字段应至少包含一个getter、一个setter 或一个notifier
”
“ 字段的 getter应是Request/Response调用,在请求消息中payload为空,并且在响应消息的有效载荷中具有该字段的值
”
“ 字段 setter应是Request/Response调用,请求消息的payload中有需要设置的值,响应消息的payload中有设置的值
”
“ 当客户端订阅该 字段
时,notifier应发送一个事件消息,将字段的值传输到客户端
”
从上面可以看出,请求/响应消息既可以远程调用方法,也可以调用字段的getter和setter
而Notification Event和Field notifer的区别,就是后者在订阅后会发送一个当前值
客户端不管是请求还是订阅,都有可能在服务器中遭遇失败,这也就意味着服务器需要具备错误处理的能力
错误处理可以在应用程序或下面的通信层中完成,因此,SOME/IP
支持两种不同的处理错误的机制:
具体用哪个,取决于配置情况
“ Return Code是将应用程序错误和方法的响应数据从服务器传输到客户端
”
请注意,从SOME/IP的角度来看,Request和Response方法的Return Code不会被视为错误,这意味着如果请求/响应方法以不等于0x00的返回码退出,则消息类型仍为0x80
什么意思
也就是说当Return Code不为0x00时,也可以用Response来反馈这个Error Code,而不是用Message Type为0x81的Error Message
“ 显式错误消息将 应用程序错误
和响应数据或通用SOME/IP错误从服务器传输到客户端
”
“ 如果需要传输更详细的错误信息,则错误消息(Message Type 0x81)的Payload应填充特定错误数据,例如异常字符串
, 应发送错误消息而不是响应消息
”
也就是说,如果服务器需要发送详细的错误信息给客户端,那么就使用错误消息而非响应消息,因为错误消息可以携带错误信息,而不是简单的Error Code
“ 所有的消息都有Return Code字段,只有Response消息(Message Type 0x80)和Error消息(Message Type 0x81)使用Return Code字段承载Return Code值回复请求(Message Type 0x00), 除0x80和0x81之外的所有其他消息都应将Return Code字段设置为0x00
”
上面讲SOME/IP首部格式时讲过,这里不再细说
“ SOME/IP消息的接收者不应对events/notifications回复错误消息
”
“ SOME/IP消息的接收者不应对fire&forget methods回复错误消息
”
“ 如果events/notifications和fire&forget methods的Message Type字段被错误地设置成Request或Response,则SOME/IP消息的接收者不应回复错误消息
”
“ 对于请求/响应方法,错误消息应复制请求/响应消息的SOME/IP首部的字段(例如Message ID、Request ID和Interface Version)而不是Payload,此外Message Type(0x81)和Return Code必须设置为适当的值
”
对于通过UDP接收的SOME/IP消息,应检查UDP如下信息:
在考虑RPC消息的传输时,存在不同的可靠性语义:
“ SOME/IP目前在使用UDP绑定时实现了“可能”的可靠性,而在使用TCP绑定时实现了“恰好一次”的可靠性,进一步的错误处理留给应用程序
”
对于“可能”的可靠性,当使用请求/响应通信结合UDP作为传输协议时,只需要一个超时
图4.22显示了“可能”可靠性的状态机,客户端的SOME/IP实现必须等待响应指定的超时时间
如果超时发生,SOME/IP应向客户端应用程序发送E_TIMEOUT信号
对于“恰好一次”的可靠性,可以使用TCP绑定,因为TCP被定义为允许可靠的通信
Interface Version标识payload格式,payload格式受以下因素影响:
接口版本会受以下原因增加:
接口版本不会因为payload格式的兼容变化而增加
以上内容参考文档AUTOSAR_PRS_SOMEIPProtocol.pdf