ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SOME/IP协议全解析:面向服务的车载以太网通信中间件

SOME/IP协议全解析:面向服务的车载以太网通信中间件 先说个背景。这几年做车载通信相关的项目我发现一个很有意思的现象很多刚入行的人听说SOME/IP第一反应是又一个新协议第二反应是它和CAN到底什么关系第三反应往往就卡住了——因为到处都是标准文档、原理图、报文格式但真要说清楚这玩意儿到底解决了我什么问题能说明白的人其实不多。恰好我这些年一直在做车载以太网和SOA通信架构相关的工作SOME/IP几乎天天打交道。今天就用一篇相对完整的总结把SOME/IP从头到尾讲透它是什么、为什么存在、核心机制是怎么运转的、你在实际项目里怎么把它用起来、以及最常见的那些坑都在哪儿。这篇文章不是概念复读机而是站在我要在项目里真正落地这套协议的角度来写的。不管你是刚接触车载以太网的嵌入式工程师还是做自动驾驶域控制器软件架构的同行又或是想搞明白SOA通信怎么实现的测试同学看完之后应该都能对SOME/IP建立起一套完整、可落地的认识。1. SOME/IP到底是什么解决汽车通信的什么问题1.1 从CAN到以太网为什么需要一套新协议要说清楚SOME/IP得先回到汽车电子电气架构演进这条线上。传统汽车的功能是典型的信号导向Signal-based设计。ECU之间靠CAN、LIN这类总线通信大家在设计阶段就把每个信号定义好某个报文ID对应哪些信号每个信号在哪个字节、哪个位、什么偏移量、什么精度全部提前写死在规范里。这套方式在几十个ECU的时代够用但到了今天智能座舱、智能驾驶这些新功能上来之后问题就非常明显了。首先是带宽不够。CAN-FD的带宽也就几Mbps到十几Mbps而一个高清摄像头、一个激光雷达的数据量动辄几百Mbps甚至更高。哪怕图像数据不走CAN但控制类、状态类的信息交互量也比以前大了几个量级。其次是软件架构的僵化。传统方式下信号的收发是静态绑定的。某个ECU要新增一个功能或者要跟另一个ECU交互一段新的数据往往需要重新定义报文、重新分配信号、更新所有相关节点的配置——改一个信号牵一发动全身。第三是部署灵活性差。传统的ECU功能固定一旦出厂就很难OTA升级新增功能更谈不上按需动态调用某个服务。这三个痛点叠加在一起行业就形成了共识下一代车载通信架构必须走以太网 面向服务SOA的路线。以太网解决带宽问题SOA解决软件解耦和动态调用问题。但光有以太网和IP协议栈还不够因为传统的HTTP、MQTT这些应用层协议是为互联网场景设计的它们的实时性、确定性、资源开销等特性并不完全适配车载环境。SOME/IP就是在这样一个背景下出现的。1.2 面向服务与面向信号的本质差别先看一张对比表。这不是为了罗列差异而是为了把两边的心智模型说清楚。维度面向信号传统CAN时代面向服务SOME/IP时代通信单元信号Signal服务Service定义方式报文ID 信号位服务接口方法/事件/字段调用关系静态配置固定收发动态发现按需调用耦合程度强耦合改一处影响一片弱耦合提供方与消费方解耦扩展方式重新设计报文和节点新增服务或新增服务实例数据量级小数据周期发送可大可小事件驱动为主传统信号模式很像一栋楼的信件收发室每户一个信箱邮递员按固定路线投递你不需要知道谁寄的只需要每天定时开信箱取信。这套模式效率不低稳定性也好但问题是路线是死的信箱是固定的今天想多收一份报纸得重新规划整条路线。而SOA模式更像手机上的APP调用云端API。你的APP不需要提前知道服务器在哪、用什么语言写的只需要根据服务契约比如API文档发起请求就能拿到想要的数据。服务挂了你换个地址重新请求就行服务增加了新功能旧功能仍然可以照常调用。SOME/IP就是车载以太网上的这层API调用机制。它把整车的功能模块抽象成一个一个的服务单位——例如某个域控制器提供360环视服务另一个域控制器提供倒车辅助服务——然后通过一套标准的发现、订阅、调用协议把这些服务高效地组织起来。1.3 拆开名字看门道Scalable / Service-Oriented / Middleware / over IPSOME/IP的全称是Scalable service-Oriented MiddlewarE over IP。名字里每一个词都有讲究不是随便拼出来的。Scalable可扩展这是应对车辆电子系统复杂度增长的关键诉求。服务可以灵活新增服务实例可以动态调整通信模式可以从请求/响应平滑扩展到事件订阅系统可以从小到大逐步成长而不需要推翻重来。Service-Oriented面向服务通信的基本单元不再是一个报文而是一项服务。一个服务里可以包含方法调用Method、事件通知Event、字段读写Field等多种交互类型。这套抽象能力跟IT领域的微服务架构一脉相承只不过跑在嵌入式环境里有着更严格的资源约束和实时性要求。MiddlewarE中间件SOME/IP不是硬件层协议也不是纯应用层业务逻辑它处在操作系统上层、应用之下屏蔽了底层IP网络的差异向上提供一个相对统一的服务调用视图。对应用开发者来说调用一个SOME/IP服务感觉上就像调用一个本地函数。over IP跑在IP网络上底层传输可以走UDP也可以走TCP网络层用标准IP协议。这意味着SOME/IP天然适配车载以太网能够利用现有的网络管理和诊断基础设施。这套设计决定了SOME/IP的江湖地位它是AutoSAR APAdaptive Platform中通信栈的关键组成部分也是如今汽车服务化架构里事实上的标准通信协议之一。用得越来越多值得花时间把它吃透。2. 第一个核心机制服务发现Service Discovery2.1 服务发现到底在干什么如果说SOME/IP是一套服务调用的电话系统那服务发现就是那本电话簿。它的作用是让服务的提供方Service Provider和消费方Service Consumer在动态变化的网络环境里互相找到对方并且建立正确的通信关系。为什么需要动态发现因为SOA架构的前提就是解耦——消费方不需要知道提供方的IP地址、端口号、实例ID只需要知道我要找什么类型的服务。而提供方可能不止一个实例也可能随时上线、下线。如果所有信息都靠静态配置那又退回传统信号模式的老路了。SOME/IP的服务发现SD定义在单独的协议文档里这一层专门处理三件事发现远程服务有没有。监控这些服务的状态可用、不可用、订阅状态。动态建立和结束服务之间的订阅关系。2.2 查找、提供、订阅三个阶段几个关键报文SD的过程可以大致拆成三个阶段阶段一服务查找Service Discovery消费方启动后会向外发送一个查找服务的报文比如FindService(service_id0x1234)。这个报文通常以UDP广播或多播的形式发出去告诉网络里的所有节点我要找这个服务谁在提供请回答。阶段二服务应答Service Offer如果某个节点确实提供了这个服务它就会收到查找报文并周期性发送OfferService(service_id0x1234, instance_id0x0001)报文把自己的服务ID、实例ID、IP端口等信息广播出去。这有点像你要找一家餐厅搜了一圈之后符合条件的餐厅挨个在大众点评上报到。这里有个细节OfferService也不一定非得等收到FindService才发。很多系统里服务提供方启动后会直接周期性广播OfferService消费方只需要监听就行。两种触发机制主动查找/被动监听同时存在也是设计上为了提高发现效率。阶段三订阅Subscribe发现服务只是第一步真正要拿数据得订阅。消费方确认服务可用后会发送SubscribeEventgroup报文表示我要订阅这个服务的某组事件。提供方校验权限、检查版本兼容之后回复SubscribeEventgroupAck成功或SubscribeEventgroupNack失败。订阅成功后提供方就会开始向消费方推送事件通知。这个流程看着简单实际项目里却是个高频排查点。因为很多问题不是出在数据传输上而是出在SD这一层——服务没被发现后面的一切都无从谈起。2.3 常见配置参数和避坑点在命令行实战中SD相关的报文、端口号、周期参数通常是通过配置文件来管理的。这期间有几个关键参数需要特别注意SD端口号SOME/IP SD默认运行在UDP 30490端口实际项目中可以改但消费方和提供方必须保持一致否则服务永远发现不了。OfferService周期默认通常是1000ms到3000ms重发一次具体看协议栈实现。如果系统资源紧张或者不需要频繁发现新节点可以调大这个周期但会影响新节点上线的发现速度。初始等待时间Initial Delay网络刚启动时不要立刻猛发FindService建议等待一定时间比如100ms等网络稳定并且OfferService已经开始广播后再进入监听状态否则容易造成启动风暴。订阅超时订阅报文发出后如果长时间没有收到Ack需要在超时后重试但次数要限制避免在服务异常时反复刷屏。注意SD报文本身走UDP是无连接不可靠的所以SD里的所有关键交互比如订阅请求/应答都需要靠超时和重传机制来保证可靠性。千万不要在SD层用TCP来兜底SD的设计本身就是基于UDP的。3. 第二个核心机制报文格式与数据序列化3.1 SOME/IP报文头详解服务发现解决的是我怎么找到服务而一旦找到服务后续的通信就要靠SOME/IP协议本体的报文来承载。报文格式是整个协议最底层的部分所有上层能力都建立在这套固定格式上。SOME/IP报文头固定32字节这是硬性规定所有实现必须遵守。核心字段包括字段名长度说明Message ID32 bit服务ID16bit 方法ID16bit标识具体属于哪个服务的方法或事件Length32 bit从Request ID开始到Payload末尾的总长度Request ID32 bit客户端ID16bit 会话ID16bit用于匹配请求和响应Protocol Version8 bit协议版本当前固定为0x01Interface Version8 bit服务接口版本Application独立定义Message Type8 bit请求/响应/通知/错误等消息类型Return Code8 bit返回值0x00表示成功Payload可变实际业务数据这里面精华不在字段定义而在Message ID Request ID这套组合的语义。你可以这样理解Message ID就是接口名告诉接收方这条消息是哪项服务的哪个方法。服务ID和方法ID合在一起全局唯一。Request ID里的Session ID帮助实现请求-响应的关联。客户端发出一个请求带着一个自增的Session ID服务端响应时Response必须带上同样的Session ID这样客户端才能确认这条响应对应我哪条请求。这套映射关系跟HTTP的Request ID思路类似只不过在嵌入式场景里字段更紧凑处理逻辑更精简。3.2 请求/响应、FireForget、事件通知、Field四种通信模式SOME/IP之所以灵活在于它同时支持多种通信模式。核心有四种1. Method方法调用—— 请求/响应模式这是最标准的模式。客户端发送一个请求服务端处理完后返回一个响应。比如ECU A请求ECU B解锁车门B执行动作后返回已解锁。这种模式必须支持错误码返回方便调用方判断执行结果。2. Method方法调用—— FireForget模式只看名字就很好懂发出请求不需要等待响应。适合那些你执行就行不需要给我反馈的场景比如一次性的故障复位、日志触发。这个模式对可靠性的要求取决于上层业务如果丢一条会导致状态不一致那就不太适合用这个模式。3. Event事件通知服务端主动向订阅者推送数据。典型的场景是传感器数据方向盘转角、车速、电池温度这些数据有变化就会推给订阅的ECU。订阅关系在SD订阅阶段就已经建立本地不需要再重复配置。4. Field字段Field可以理解为可以读写的属性。它组合了Getter读取、Setter写入和事件通知三种能力。比如当前车内温度是一个字段你可以读取它可以设置目标温度也可以订阅它来接收温度变化事件。这四种模式让开发者可以用一套协议覆盖几乎所有应用交互类型。设计业务模型的时候我习惯先梳理每个交互是一次性请求还是持续推送是需要确认还是不需要确认然后据此选择对应的通信模式而不是无脑全用MethodResponse。3.3 数据序列化规则字节序、对齐、TLV报文头的格式是固定的但Payload里的业务数据如何编码SOME/IP也有自己的规范这层统称为序列化Serialization。序列化核心规则可以归纳成几条遵循C数据类型布局SOME/IP的序列化规则跟C/C的struct布局一脉相承。整数、浮点数按固定大小编码字符串、数组、结构体按顺序排列。大端字节序SOME/IP默认采用Big-Endian网络字节序编码。对齐要求对结构体内部成员默认按4字节对齐。这是很多新手容易踩的坑——你以为发的是一个紧凑的二进制流实际协议栈会按对齐规则插入填充字节如果两端实现不一致解析出来就是一堆乱码。动态长度字段数组、字符串等变长数据前面会加上长度字段通常是32位长度表示后续字节数。这个序列化规则看着简单实际项目里一旦数据结构复杂起来——嵌套结构体、枚举、数组、字符串、可选项——就很容易出错。我通常建议在协议设计阶段先画好数据结构完全图包括对齐、字节序、长度字段再交给前后端分别实现避免后期反复扯皮。提示SOME/IP里还有一种扩展方案叫SOME/IP-TPTransport Protocol专门用于处理UDP承载大数据的情况。当Payload超过UDP单包能力比如超过1400字节TP会把一个SOME/IP消息拆成多个分片传输接收端再重组。这个机制是自动的但对开发者来说要特别注意超大数据结构的设计分片越多丢失风险越高重传成本也越高。4. 不同传输层的选型UDP还是TCP4.1 三种传输方式的使用场景SOME/IP底层可以跑UDP、TCP还可以通过UDP多播/广播实现高效分发。这个选择权给了开发者很大的灵活性但也带来了设计上的第一个岔路口。简单列一下我在项目里的选型逻辑通信场景推荐传输方式理由服务发现SDUDP广播/多播目的是让所有人发现无需可靠传输广播效率最高请求/响应Method通常UDP单播一次请求一次响应数据量小延迟低事件通知EventUDP单播或多播事件推送是常态UDP的高效更适合周期性小数据大数据文件传输TCP数据完整性优先TCP的可靠传输省去大量重传逻辑高可靠性控制指令TCP不能丢、不能乱TCP的流控与重传更稳妥这里并不是说UDP一定比TCP好。SOME/IP官方文档里给出的Guideline也是场景化选择。我个人的经验是控制在UDP数据在TCP。控制指令追求低延迟UDP合适批量数据追求完整性TCP合适。事件推送优先选UDP。事件本身就是丢失可容忍的设计订阅方如果丢了一个中间状态通常可以通过下一帧恢复没必要为每一条事件都付出TCP的握手和ACK开销。方法论上能UDP就UDP确实需要可靠时才上TCP。因为TCP在车载环境下会带来额外的延迟和复杂度尤其在高负载情况下TCP的拥塞控制会因为丢包而急剧降低吞吐这在实时性要求高的场景下是很头疼的。4.2 大数据的拆分与重组SOME/IP-TP前面已经提到过SOME/IP-TP这里把它的工作机制说得更细一点。当某个SOME/IP消息的Payload超过MTU通常以UDP的1472字节为界协议栈会调用TP模块把原始消息切成若干个分片Segment每个分片通过独立的UDP报文发送出去。接收端收到所有分片后按照每个分片头里的偏移量Offset和总长度信息重新组合成完整的SOME/IP消息再交给上层处理。这个机制有几个关键点分片不是随便切的第一个分片里必须带上原始的SOME/IP消息头信息和完整的Payload长度这样接收端才知道我要等这么多数据。每个分片都带一个TP头包含原始Message ID、总长度、分片序号、分片偏移等字段。Sender和Receiver两侧的TP缓冲区大小必须匹配。如果发送端切了10个分片接收端缓冲区只能存5个那就会发生重组失败最终只能丢弃整条消息。我在实际项目里遇到过一种很隐蔽的问题服务端发送一个较大的结构体比如超过4KB客户端一直收到消息但上层就是解析不出来。排查到最后才发现客户端的接收缓冲区默认只有1KBTP模块重组时直接把后面的分片当新消息处理解析自然全错。所以配置双方缓冲区时一定要比最大Payload预留足够余量。4.3 性能与可靠性的平衡最后想聊一个更宏观的话题在SOME/IP通信设计中性能与可靠性的平衡本质上靠的是选对模式。有人总觉得UDP不靠谱于是把所有通信都用TCP。但车载环境里有很多数据是丢了就丢了的——比如周期性的加速度计数据这一帧丢了下一帧马上会更新再比如UI刷新用的状态值丢失一个中间状态最终态到达即可。对这些数据用TCP反而会因为重传机制导致旧数据延迟到达对实时性有害无益。反过来对于指令类数据——远控解闭锁、控制器升级、故障码清除——可靠性必须拉满。这时候用UDP手动重传的复杂度远高于直接用TCP。所以我的建议是设计初期花两个小时把所有交互按实时性要求“可靠性要求”“数据量大小”三个维度打一遍分再决定用哪种模式。这个设计前置的习惯能帮你避免后期大量的架构返工。5. 上手实操从零模拟一套SOME/IP通信5.1 环境准备vSomeIP与Wireshark理论说得再多不如亲手跑一遍。我推荐两个工具组合vSomeIP做协议栈Wireshark做抓包分析。vSomeIP是宝马开源的一套SOME/IP C实现是目前最接近生产级的参考实现之一代码结构清晰非常适合学习和二次开发。它支持UDP、TCP、SD、序列化等功能也自带示例应用。安装vSomeIP很简单依赖是Boost库和CMake# Ubuntu环境示例 sudo apt-get install libboost-system-dev libboost-thread-dev cmake git clone https://github.com/COVESA/vsomeip.git cd vsomeip mkdir build cd build cmake .. make -j4编译完成后示例程序会出现在examples目录下里面就有经典的sample-requestor消费方和sample-service提供方。抓包工具自然是Wireshark。新版Wireshark自带SOME/IP解析器能直接按字段解析SOME/IP报文非常方便。如果没有对应解析器可以装wireshark-dev插件或者先用抓包文件十六进制对照分析。5.2 搭建一个服务端与客户端vSomeIP的代码不算复杂核心就是注册服务、等待调用、发送事件。我用示例代码稍作改动做一个最简单的温度服务服务端提供当前温度客户端请求一次就拿到温度值。服务端核心逻辑伪代码示意#include vsomeip/vsomeip.hpp class temperature_service { public: temperature_service() : app_(vsomeip::runtime::get()-create_application(temp-service)) {} void init() { app_-init(); app_-register_message_handler( SERVICE_ID, INSTANCE_ID, METHOD_ID_GET_TEMP, std::bind(temperature_service::on_get_temp, this, std::placeholders::_1)); app_-offer_service(SERVICE_ID, INSTANCE_ID); app_-start(); } void on_get_temp(const std::shared_ptrvsomeip::message req) { auto resp vsomeip::runtime::get()-create_response(req); // 在payload里写入温度值 std::string temp 25.5C; resp-set_payload(vsomeip::runtime::get()-create_payload( temp.begin(), temp.end())); app_-send(resp); } private: std::shared_ptrvsomeip::application app_; };客户端核心逻辑伪代码示意#include vsomeip/vsomeip.hpp class temp_client { public: temp_client() : app_(vsomeip::runtime::get()-create_application(temp-client)) {} void init() { app_-init(); app_-register_message_handler( SERVICE_ID, INSTANCE_ID, METHOD_ID_GET_TEMP, std::bind(temp_client::on_response, this, std::placeholders::_1)); app_-request_service(SERVICE_ID, INSTANCE_ID); app_-start(); // 等SD完成之后发送请求 std::this_thread::sleep_for(std::chrono::seconds(2)); auto req vsomeip::runtime::get()-create_request(); req-set_service(SERVICE_ID); req-set_instance(INSTANCE_ID); req-set_method(METHOD_ID_GET_TEMP); app_-send(req); } void on_response(const std::shared_ptrvsomeip::message resp) { auto payload resp-get_payload(); std::string temp(payload-get_data(), payload-get_length()); std::cout Temperature: temp std::endl; } private: std::shared_ptrvsomeip::application app_; };这里有个关键点request_service之后客户端不会立刻就能发请求必须等待SD完成服务发现。示例里用sleep来等实际开发中应该用register_state_handlerregister_availability_handler来处理服务可用事件避免硬编码延时。5.3 抓包分析一条完整的服务发现过程跑起来服务端和客户端后用Wireshark在回环网卡上抓包。打开抓包文件你应该能看到这么一串节奏的报文客户端启动后发出FindService报文目的地址是组播地址比如239.192.255.250:30490报文内容是谁在提供温度服务服务端收到后回复或直接周期性发送OfferService报文告诉客户端我在这里并附带上服务ID、实例ID、通信所需的IP和端口。客户端收到OfferService后发送SubscribeEventgroup如果它要订阅事件服务端回复SubscribeEventgroupAck。之后两端就可以正常进行Method调用了。用Wireshark查看时重点关注这几个字段Message ID区分服务和方法Message Type区分请求Request、响应Response、通知Notification、错误ErrorSession ID请求和响应的关联线索Interface Version版本不匹配也会导致调用失败应该能直观看到整个流程和前面介绍SD机制的阶段划分完全吻合。多跑几次试着抓一下异常链路——比如关掉服务端再发请求你就能看到客户端的重传和超时流程这些在真实项目排障时都是必备素材。6. 常见问题与排查技巧实录6.1 服务一直无法被订阅怎么办这是最常遇到的一种坑现象是客户端调服务端的方法始终没有响应。排查思路我建议按下面顺序走确认服务端有没有offer_service。看服务端日志确认它已经调用了offer_service如果没调用SD就不会发出OfferService客户端永远发现不了。确认网络地址和端口一致。检查两端配置的SD端口、组播地址是否一致。组播地址不一致广播收不到一切白搭。确认服务ID、实例ID匹配。客户端请求的service_id和instance_id必须和服务端提供的完全一致。确认消息类型和错误码。抓包看SD阶段有没有SubscribeEventgroupAck没Ack就是订阅失败了。这时要看Return Code字段不同的错误码对应不同问题。这里最容易忽略的是服务ID和实例ID配错了。很多SDK的示例代码里SERVICE_ID和INSTANCE_ID就在一个头文件里改客户端时漏改了其中一个就导致两端对不上。我的习惯是在集成前写一个grep脚本把项目里所有引用SERVICE_ID的地方统一核对一遍能省不少时间。6.2 报文解析乱码的排查思路如果SD通了但收到的Payload解析出来一堆乱码大概率是序列化两端不一致。排查步骤先检查字节序。SOME/IP默认大端如果你的代码参考了小端平台的示例或者你的嵌入式平台本身是小端那你就要在序列化时做字节序转换。这也是继对齐问题之后的第二大头。再检查对齐规则。SOME/IP的结构体默认4字节对齐。如果你的数据结构里有一个bool1字节后面跟一个uint32那么序列化时bool后面会有3个填充字节。客户端如果没按同样的对齐规则去读解析必然错位。要验证这点可以把两端的原始十六进制dump出来对比看看中间是不是存在0x00填充字节。最后检查长度字段。数组、字符串的动态长度在SOME/IP里通常是4字节长度前缀如果你在定义结构体时没有显式添加长度字段或者用了别的方式比如用固定长度来处理变长数据也会造成解析错乱。注意遇到解析乱码不要急着改代码。先把两端的原始Payload用hex格式打印出来做对比通常很快就能定位问题在字节序、对齐还是长度字段上。调试这种问题最怕的就是在没看清原始数据的情况下反复改逻辑。6.3 三个容易忽略的隐藏坑这三个坑是我在项目里实打实踩过的常规文档里也不太会写坑一SD的周期广播在网络割裂环境下的影响如果系统存在多个网段或者VLAN隔离SD的组播报文可能无法跨VLAN传播。很多项目里服务端和客户端明明在同一个物理交换机上但处于不同VLAN结果两边互相看不见。解决方法是配置SD代理服务或者在部署时保证服务发现报文能路由到目标网段。坑二订阅事件过多导致网络风暴如果系统里存在大量服务且每个服务的事件订阅者又很多那么事件推送在高频场景下会形成网络风暴。例如一个转速信号以100Hz推送200个ECU订阅每秒就是20000条报文加上以太网层面还有ARP、VLAN Tag、IP/TCP头部等开销交换机不一定扛得住。设计时一定要对事件推送的频率和订阅者数量做评估必要时把独立的事件合并成批量事件周期推送。坑三分片缓冲区必须大于最大消息体前面提到过SOME/IP-TP。如果你要支持大于1472字节的大消息接收端的TP缓冲区必须配置为大于该消息的总长度。否则接收端在TP层就会重组失败上层永远拿不到完整数据。这个配置项一般在协议栈的json配置里叫buffer-size-shm或直接叫tp-reassembly-buffer。在部署前用最大Payload的示例跑一次大消息收发确认不丢不分片是最稳妥的验证方式。结尾我个人在实际项目里最大的感受是SOME/IP本身并不难难的是它背后的那套SOA思维。如果你还停留在报文、信号、位域的老思路里碰到SOME/IP的第一反应会是好复杂不如CAN直接但一旦你切换到服务、实例、订阅、事件这套新心智模型很多以前需要靠静态配置和大量接线才能实现的系统现在用一套协议就干净利落地解决了。最后再分享一个小技巧刚开始上手SOME/IP我建议别急着在自己的项目里大规模铺开先找个简单的Service比如传输一个温度值、一个开关状态把完整的SD、Method、Event全流程跑一遍再逐步增加复杂度。这个过程看上去很基础但能帮你把SOME/IP的底子彻底打好后面处理几十个服务的复杂架构时就会从容很多。
返回列表