VSOMEIP主要流程的时序
请求服务:
client应用:
application_impl::request_service
routing_manager_client::request_service (老版本是routing_manager_proxy)
routing_manager_client::send_request_services
protocol::request_service_command its_command; // 创建订阅cmd
local_uds_client_endpoint_impl::send // 将订阅cmd发送给routingmanagerd
routingmanagerd:
local_uds_server_endpoint_impl::connection::receive_cbk // UnixDomainSocket /tmp/vsomeip-0 收到应用发来的request service命令
routing_manager_stub::on_message
routing_manager_impl::request_service
endpoint_manager_impl::find_or_create_remote_client // 从remote_service_info_中查找远端服务的配置,创建client_endpoint
tcp_client_endpoint_impl::start // 启动线程进行connect操作 (创建并且打开asio socket,设置socket属性,最后async_connect)
udp_client_endpoint_impl::start // 同上
在vsomeip中,每一个服务只占用一个端口(TCP/UDP/BOTH),无论method还是event都在这个端口上通信。
client发给routingmanagerd的cmd中,包含了client的端的clientid
请求事件:
client应用:
application_impl::request_event (service, instance, event, eventgroup事件组集合, 事件类型, reliable) //事件类型event/field/select_event
routing_manager_client::register_event
routing_manager_client::send_register_event
protocol::register_events_command its_command; // 注册event的cmd
local_uds_client_endpoint_impl::send // 将注册event的cmd发送给routingmanagerd
routingmanagerd:
local_uds_server_endpoint_impl::connection::receive_cbk // UnixDomainSocket /tmp/vsomeip-0 收到应用发来的request event命令
routing_manager_stub::on_message
轮询protocol::register_events_command中的每一个register event项目
routing_manager_impl::register_shadow_event
routing_manager_base::register_event //将事件和事件组注册到内部的events_ 和eventgroups_ 中
// routing_manager_base内部events_ 和eventgroups_ 的作用
primitive_types.hpp
...
typedef uint32_t message_t;
typedef uint16_t service_t;
typedef uint16_t method_t;
typedef uint16_t event_t;typedef uint16_t instance_t;
typedef uint16_t eventgroup_t;
...
event.hpp
...
class event {...
private:...// 记录了每个eventgroup有哪些本地client订阅了// 每个event可以属于一个或者多个eventgroupstd::map<eventgroup_t, std::set<client_t> > eventgroups_;...
};
...
eventgroupinfo.hpp
class eventgroupinfo {
...
private:...std::set<std::shared_ptr<event> > events_; // 记录了该eventgroup中的所有event(event中可以找到订阅该eventgroup的client)...std::map<remote_subscription_id_t,std::shared_ptr<remote_subscription>> subscriptions_; // 保存对其他域的服务的订阅者的信息remote_subscription_id_t id_; // 每增加一个对其他域的服务的订阅者(非域内), id_ 加一...
};
routing_manager_base.hpp
std::map<service_t, // 服务IDstd::map<instance_t, // 实例IDstd::map<event_t, // 事件IDstd::shared_ptr<event> > > > events_; // 事件所属的组和每个组的订阅客户端std::map<service_t, // 服务IDstd::map<instance_t,// 实例IDstd::map<eventgroup_t, std::shared_ptr<eventgroupinfo> > > > eventgroups_; // 事件组中的事件和每个事件的订阅者信息
订阅:
client应用:
application_impl::subscribe
routing_manager_client::subscribe
routing_manager_client::send_subscribe
protocol::subscribe_command its_command; // 订阅cmd
local_uds_client_endpoint_impl::send // 将订阅的cmd发送给routingmanagerd
routingmanagerd:
local_uds_server_endpoint_impl::connection::receive_cbk // UnixDomainSocket /tmp/vsomeip-0 收到应用发来的subscribe命令
routing_manager_stub::on_message
routing_manager_impl::subscribe
const client_t its_local_client = find_local_client(_service, _instance); // 获取是哪个client发布了要订阅的服务
// 1. 订阅的服务是当前routingmanagerd应用发布的
if (get_client() == its_local_client) {
endpoint_manager_base::find_or_create_local // 根据订阅者的clientid创建用于通信的local
application_impl::on_subscription // 通知application有订阅到达,是否accept (accept_cbk是传入的匿名函数)
routing_manager_stub::send_subscribe_ack // 接收订阅,则回复ACK
routing_manager_stub::send_subscribe_nack // 否则回复NACK
}
// 2. 订阅的服务不是当前routingmanagerd应用发布的
routing_manager_base::insert_subscription
if (_event != ANY_EVENT) { // subscribe to specific event
// 在event_中插入订阅client的订阅记录
} else {
// 在eventgroups_的每个event中插入订阅client的订阅记录
}
// 2.1 订阅的服务是域外的someip应用发布的
if (0 == its_local_client) { // 说明域内找不到提供该服务的ciient
routing_manager_impl::find_event // 查看是否有client对该event进行过register(调用request_event)
routing_manager_impl::handle_subscription_state //主要是看对该event的订阅是否已经有收到过ACK了
if (its_state->second == subscription_state_e::SUBSCRIPTION_ACKNOWLEDGED) {
// Subscription already acknowledged!
if (_client == get_client()) {
// 如果订阅该服务的是routingmanagerd应用自己,则直接回调
application_impl::on_subscription_status(_service, _instance, _eventgroup, _event, 0x0 /OK/);
} else if (stub_) {
// 如果订阅该服务的是域内的其他应用,则通过uds将ACK消息发送过去
routing_manager_stub::send_subscribe_ack(_client, _service, _instance, _eventgroup, _event);
}
}
service_discovery_impl::subscribe // 通过SD模块订阅其他域发布的待订阅service/event/eventgroup
subscribed_.find //查找之前是否订阅过该eventgroup
std::shared_ptr<subscription> its_subscription = create_subscription() // 创建新的订阅
subscribed_[service] [inst] [eventgroup] = its_subscription;
service_discovery_impl::send_subscription
entry_data_t its_data; //创建要订阅的eventgroup的entry (create_eventgroup_entry)
auto its_current_message = std::make_shared<message_impl>(); // 创建要发送的OFFER报文
add_entry_data(its_messages, its_data); // 将订阅的eventgroup entry添加到OFFER报文中 service_discovery_impl::serialize_and_send // 发送
}
// 2.2 订阅的服务是域内其他someip应用发布的
else {
routing_manager_stub::send_subscribe //通过UDS 发送订阅请求给发布该service/Instance的client
}
发布服务:
client应用:
application_impl::offer_service
routing_manager_client::offer_service // 参数中带着clientid以及service的信息
routing_manager_base::offer_service // 判断一下是否可以发布service (例如局域网中已经有别的client发布了相同的service/instance)
routing_manager_client::send_offer_service
protocol::offer_service_command its_offer; // 创建发布服务的cmd
local_uds_client_endpoint_impl::send // 将offer service的cmd发送给routingmanagerd
routingmanagerd:
local_uds_server_endpoint_impl::connection::receive_cbk // UnixDomainSocket /tmp/vsomeip-0 收到应用发来的offer service命令
routing_manager_impl::offer_service
routing_manager_impl::insert_offer_command // 插入OFFER队列 (个人理解用于后期定周期发送)
routing_manager_impl::handle_local_offer_service // 判断是否可以Offer Service (是否存在冲突),可以offer的情况下插入local_services_中
service_discovery_impl::offer_service // 如果collected_offers_中不存在要发布的service,则添加进去
service_discovery_impl::on_offer_debounce_timer_expired // debounce定时器触发,将collected_offers_的offerentry倒出来后清空
std::shared_ptr<message_impl> its_message(std::make_shared<message_impl>()); // 创建要发送出去的Offer报文
service_discovery_impl::insert_offer_entries //将要offer的所有service entry加入Offer报文
service_discovery_impl::insert_offer_service // 添加单个service entry (service的entry以及option,option中包含了用于通信的endpoint信息)
service_discovery_impl::add_entry_data
service_discovery_impl::send // 发送Offer报文
offerservice有三种冲突的情况下,会offer失败:1. local存在相同服务 2. remote发布过相同服务 3. 当前应用本次offer的服务和之前offer的服务存在冲突
处理收到的Offer报文:
routingmanagerd:
routing_manager_impl::on_message
service_discovery_impl::on_message // 需要是SD报文才会给SD模块处理
service_discovery_impl::process_serviceentry // 判断entry’类型是service还是eventgroup的条件是type <= 2
service_discovery_impl::process_offerservice_serviceentry // service entry条目中type=1 (offer Service)
service_discovery_impl::update_request // 如果目前处于repetition阶段,则停止对该service发送FIND报文(因为已经收到了offer了)
if (_received_via_mcast) { // 如果是从组播地址收到的OFFER报文
auto found_service = subscribed_ .find(_service); // 从 subscribed_ 成员中查找本域中是否有client对该服务中事件组的订阅
更新每一条订阅记录的状态
ST_ACKNOWLEDGED -> ST_RESUBSCRIBING
非ST_ACKNOWLEDGED -> ST_RESUBSCRIBING_NOT_ACKNOWLEDGED
}
routing_manager_impl::add_routing_info // 新增/更新serviceinfo信息到services_remote_和services_两个内部容器
std::shared_ptr<serviceinfo> its_info(find_service(_service, _instance)); // 判断是否收到过该service的offer
if (!its_info) { // 没有是收到过该service的offer
routing_manager_base::create_service_info // 创建新的serviceinfo信息,添加到services_以及services_remote_中
}
endpoint_manager_impl::is_remote_service_known // 判断是否在该service上创建endpoint(reliable/unreliable)
if (_reliable_port != ILLEGAL_PORT && !is_reliable_known) { // service提供了tcp端口,对应的endpoint没有创建
for (const client_t its_client : get_requesters_unlocked(
endpoint_manager_impl::find_or_create_remote_client // 创建和service的tcp连接
its_info->add_client(its_client); // 将请求该service的client添加到上面create_service_info创建的serviceinfo中
}
}
if (_unreliable_port != ILLEGAL_PORT && !is_unreliable_known) { // service提供了udp端口,对应的endpoint没有创建
// 逻辑同TCP
}
if (!_reliable_address.is_unspecified() || !_unreliable_address.is_unspecified()) {
// 记录每个远端地址上发布的服务信息和收到的Offer报文数量并且打印
}
client应用:
无:
entry条目类型的枚举值: (enumeration_types.hpp)
enum class entry_type_e: uint8_t {FIND_SERVICE = 0x00,OFFER_SERVICE = 0x01,STOP_OFFER_SERVICE = 0x01,REQUEST_SERVICE = 0x2,FIND_EVENT_GROUP = 0x4,PUBLISH_EVENTGROUP = 0x5,STOP_PUBLISH_EVENTGROUP = 0x5,SUBSCRIBE_EVENTGROUP = 0x06,STOP_SUBSCRIBE_EVENTGROUP = 0x06,SUBSCRIBE_EVENTGROUP_ACK = 0x07,STOP_SUBSCRIBE_EVENTGROUP_ACK = 0x07,UNKNOWN = 0xFF
};
service_discovery_impl中保存的事件组订阅情况的类Subscription主要结构如下:
class subscription {
...
private:std::shared_ptr<endpoint> reliable_;std::shared_ptr<endpoint> unreliable_;bool tcp_connection_established_;bool udp_connection_established_;std::map<client_t, subscription_state_e> clients_; // client-> is acknowledged? // 每个client的订阅状态std::weak_ptr<eventgroupinfo> eg_info_; // 订阅的事件组信息
};
routing_manager_impl中保存的收到的service信息的类serviceinfo主要结构如下:
class serviceinfo {
...
private:service_t service_;instance_t instance_;major_version_t major_;minor_version_t minor_;std::shared_ptr<endpoint> reliable_;std::shared_ptr<endpoint> unreliable_;std::set<client_t> requesters_; // 请求过该服务的本地client
};
服务可用通知(Service Aailable)
routingmanagerd:
client_endpoint_impl::cancel_and_connect_cbk // 作为async_connect的回调函数被调用
client_endpoint_impl::connect_cbk
endpoint_manager_impl::on_connect
routing_manager_impl::service_endpoint_connected
routing_manager_impl::on_availability
routing_manager_stub::on_offer_service // 通知客户端sevice avaliable
routing_manager_stub::inform_requesters // routing_info_entry_type_e::RIE_ADD_SERVICE_INSTANCE
for (auto its_client : service_requests_) { // 该连接是用于和远端service通信的,找到请求了对应service的本地client
routing_manager_stub::send_client_routing_info
protocol::routing_info_command its_command; // 发送给client的routing_info信息(包含了service信息)
}
client应用:
routing_manager_client::on_message
routing_manager_client::on_routing_info
for (const auto &e : its_command.get_entries()) { // 轮询每一个entry (号到type为RIE_ADD_SERVICE_INSTANCE的条目)
application_impl::on_availability // 通知application服务可用
}
处理订阅:
routingmanagerd:
service_discovery_impl::on_message
auto its_acknowledgement = std::make_shared<remote_subscription_ack>(_sender); //提前准备好给客户端回复的订阅ACK/NACK报文
service_discovery_impl::process_eventgroupentry
service_discovery_impl::insert_subscription_ack
service_discovery_impl::add_entry_data_to_remote_subscription_ack_msg
service_discovery_impl::send_subscription_ack
client应用:
无
处理订阅ACK/NACK
routingmanagerd:
service_discovery_impl::on_message
service_discovery_impl::process_eventgroupentry(
if (entry_type_e::SUBSCRIBE_EVENTGROUP_ACK == its_type) { // 对于eventgroupentry类型为subscribe_ack/nack
service_discovery_impl::handle_eventgroup_subscription_ack/_nack (根据Entry中的TTL判断是ACK还是NACK)
auto found_service = subscribed_.find(_service);
for (…) { // 找到每一个订阅该eventgroup的client
found_eventgroup->second->set_state(its_client, subscription_state_e::ST_ACKNOWLEDGED); // 修改订阅状态 (ACKED)
routing_manager_impl::on_subscribe_ack(_client)
}
}
client应用:
发布事件:
client应用:
routingmanagerd:
发送REQUEST:
client应用:
routingmanagerd:
发送RESPONSE:
client应用:
routingmanagerd:
相关文章:
VSOMEIP主要流程的时序
请求服务: client应用: application_impl::request_service routing_manager_client::request_service (老版本是routing_manager_proxy) routing_manager_client::send_request_services protocol::request_service_command its_command; // 创建…...
右值引用和移动语义:
C 右值引用和移动语义详解 在 C 的发展历程中,右值引用和移动语义的引入带来了显著的性能提升和编程灵活性。本文将深入探讨右值引用和移动语义的概念、用法以及重要性。 一、引言 C 作为一门高效的编程语言,一直在不断演进以满足现代软件编程的需求。…...

经纬高LLA转地心地固ECEF坐标,公式,代码
经纬高转地心地固的目的 坐标系转换是gis或者slam系统常见操作。GNSS获取的一般是经纬高,经纬高在slam系统里无法应用,slam系统一般是xyz互相垂直的笛卡尔坐标系,所以需要把GNSS的经纬高转到直角坐标系地心地固ECEF或者高斯投影GKP。 划重点…...

VUE前端实现天爱滑块验证码--详细教程
第一步: Git地址:tianai-captcha-demo: 滑块验证码demo 找到目录 src/main/resources/static,拷贝 static 并改名为 tac 即可。 第二步: 将改为 tac 的文件,放进项目根目录中,如下图: 第三步࿱…...
【链表】【删除节点】【刷题笔记】【灵神题单】
237.删除链表的节点 链表删除节点的本质是不用删除,只需要操作指针,跳过需要删除的节点,指向下下一个节点即可! 删除某个节点,但是不知道这个节点的前一个节点,也不知道头节点!摘自力扣评论区…...

springboot339javaweb的新能源充电系统pf(论文+源码)_kaic
毕 业 设 计(论 文) 题目:新能源充电系统的设计与实现 摘 要 如今社会上各行各业,都喜欢用自己行业的专属软件工作,互联网发展到这个时候,人们已经发现离不开了互联网。新技术的产生,往往能解…...

【嵌入式——QT】QT制作安装包
第一步 QT程序写好之后,编译release版本 第二步 拿到release生成的.exe文件 第三步 新建文件夹deploy 第四步 将.exe文件复制到deploy目录下 第五步 在该目录下输入cmd指令,回车 第六步 在打开的命令窗口下输入 windeployqt TegNetCom_1.0.…...
python的文件操作练习
文件操作:成绩统计 有一个文件grades.txt,文件内容是每行一个学生的成绩(格式:姓名,成绩)。要求: 读取文件内容,统计所有学生的平均成绩; 将不及格(<60分)…...

jQuery九宫格抽奖,php处理抽奖信息
功能介绍 jQuery九宫格抽奖是一种基于jQuery库的前端抽奖效果。通过九宫格的形式展示抽奖项,用户点击抽奖按钮后,九宫格开始旋转,最终停在一个随机位置上,此位置对应的抽奖项为用户的中奖结果。 本文实现九宫格的步骤为…...

2024年一级建造师考试成绩,即将公布!
一级建造师考试成绩一般在考试结束后3个月左右的时间公布! 根据官方通知,重庆、江苏、青海、江西、云南、湖南、福建、北京、山西、黑龙江等地在今年一建报名通知里提到:2024年一级建造师考试成绩预计于2024年12月上旬公布。考生可在这个时间…...

M4V 视频是一种什么格式?如何把 M4V 转为 MP4 格式?
M4V 是一种视频文件格式,主要由苹果公司用于其产品和服务中,如 iTunes Store 上的电影和电视节目。这种格式可以包含受版权保护的内容,并且通常与苹果的 DRM(数字版权管理)技术结合使用,以限制内容的复制和…...

Leetcode 每日一题 104.二叉树的最大深度
目录 问题描述 示例 示例 1: 示例 2: 约束条件 题解 方法一:广度优先搜索(BFS) 步骤 代码实现 方法二:递归 步骤 代码实现 结论 问题描述 给定一个二叉树 root,我们需要返回其最大…...

文件上传漏洞:你的网站安全吗?
文章目录 文件上传漏洞攻击方式:0x01绕过前端限制0x02黑名单绕过1.特殊解析后缀绕过2..htaccess解析绕过3.大小写绕过4.点绕过5.空格绕过6.::$DATA绕过7.配合中间件解析漏洞8.双后缀名绕过9.短标签绕过 0x03白名单绕过1.MIME绕过(Content-Type绕过)2.%00截断3.0x00截…...

AWS账号提额
Lightsail提额 控制台右上角,用户名点开,选择Service Quotas 在导航栏中AWS服务中找到lightsail点进去 在搜索框搜索instance找到相应的实例类型申请配额 4.根据自己的需求选择要提额的地区 5.根据需求来提升配额数量,提升小额配额等大约1小时生效 Ligh…...

电子应用设计方案-29:智能云炒菜系统方案设计
智能云炒菜系统方案设计 一、系统概述 本智能云炒菜系统旨在为用户提供便捷、高效、个性化的烹饪体验,结合云技术实现远程控制、食谱分享、智能烹饪流程优化等功能。 二、系统组成 1. 炒菜锅主体 - 高品质不粘锅内胆,易于清洁和维护。 - 加热装置&#x…...
腾讯rapidJson使用例子
只需要把库的头文件拿下来加入项目中使用就行,我是以二进制文件存储内容并解析: #include <iostream> #include <fstream> #include <string> #include "rapidjson/document.h" #include "rapidjson/error/en.h"…...
UE5_CommonUI简单使用(2)
上篇我是简单写了一下CommonUI使用的初始设置以及Common Activatable Widget和Common Activatable Widget Stack以及Common 控件Style以及鼠标控制的一些内容,这些对于了解UMG的朋友来说没什么难度,唯一需要注意的就是Common Activatable Widget Stack堆栈管理只能是用来管理…...

探讨播客的生态系统
最近对播客发生了兴趣,从而引起了对播客背后的技术,生态的关注。本文谈谈播客背后的技术生态系统。 播客很简单 播客(podcast)本质上就是以语音的方式发布信息。它和博客非常类似。如果将CSDN 网站上的文字加一个语音播报。CSDN …...

淘宝架构演化
基本功能 LAMP(LinuxApacheMySQLPHP)标准架构,初期采用拿来主义,只具备基本功能。 数据库:读写分离,MyISAM存储引擎 2003年5月—2004年1月 存储瓶颈 mysql达到访问瓶颈,升级成oracle&#x…...

软通动力携子公司鸿湖万联、软通教育助阵首届鸿蒙生态大会成功举办
11月23日中国深圳,首届鸿蒙生态大会上,软通动力及软通动力子公司鸿湖万联作为全球智慧物联网联盟(GIIC)理事单位、鸿蒙生态服务(深圳)有限公司战略合作伙伴,联合软通教育深度参与了大会多项重磅…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...