vsomeip 协议栈总结

什么是 SOME/IP

Scalable service-Oriented middlewarE over IP,基于 IP 的可扩展的面向服务的中间件。
SOME/IP于2011年由BMW设计,2014年纳入AUTOSAR规范。该中间件是为典型的汽车用例而设计的。
帧结构
IP 网络上的两个设备,可以通过 SOME/IP 消息通信进行通信,传输层可基于 TCP 或 UDP。下图描述了通信过程:

在这里插入图片描述

假设设备 B 上运行一个服务,该服务提供一个函数,设备 A 通过 SOME/IP 消息调用该函数,函数的执行结果再通过 SOME/IP 消息返回给设备 A。
SOME/IP 消息由两部分组成:头部和负载。
头部字段的含义:
• Service ID: 服务唯一标识。
• Method ID: 函数的标识。
• Length: 负载长度(以字节为单位),包含头部 Length 之后的 8 字节。
• Client ID: 客户端唯一标识。
• Session ID: 会话计数值,每次通信完加一。
• Protocol Version: 0x01
• Interface Version: 服务接口主版本号
• Message Type:
○ – REQUEST (0x00) 请求消息,需要服务恢复
○ – REQUEST_NO_RETURN (0x01) 请求消息,不需要服务回复
○ – NOTIFICATION (0x02) 通知消息
○ – RESPONSE (0x80) 回复消息
• Return Code:
○ – E_OK (0x00) 没有错误
○ – E_NOT_OK (0x01) 未知错误
○ – E_WRONG_INTERFACE_VERSION (0x08) 接口版本不匹配
○ – E_MALFORMED_MESSAGE (0x09) 消息反序列化错误
○ – E_WRONG_MESSAGE_TYPE (0x0A) 消息类型错误
负载:
协议需要提供序列化和反序列化的能力。
服务可以多次实例化,每一个实例通过不同的 ID 进行标识,实例 ID 并不包含在 SOME/IP 消息头部字段中,需要通过不同的端口号区分不同的实例。

协议规范

通信模式

SOME/IP 支持两种通信模式: publish/subscribe 和 request/response。
在这里插入图片描述
request/response :是对远程过程调用标准机制的实现,客户端可以通过 request 消息调用服务端的函数,结果通过 response 消息返回。
publish/subscribe :客户端可以通过 subscribe 向服务端订阅事件,当事件发生时,服务端可以主动通知客户端事件的状态。

服务发现

SOME/IP 协议是通过 SOME/IP-SD 消息实现服务发现功能的。
在这里插入图片描述
局域网中的每个设备会定期广播(组播)包含由该设备提供的所有服务的 “offer” 消息。该消息消息通过UDP发送。客户端通过通过解析该消息可以获取服务实例的位置(ip和port)。如果客户端应用程序需要服务,但目前没有服务提供,那么也可以发送 “find” 消息。
SOME/IP-SD 协议还可以检测服务实例是否正在运行,以及实现 publish/subscribe 处理。

Vsomeip 实现

Vsomeip 使用

服务端程序:

#include <iomanip>
#include <iostream>
#include <sstream>
#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1236
#define SAMPLE_INSTANCE_ID 0x5678
#define SAMPLE_METHOD_ID 0x0421

std::shared_ptr<vsomeip::application> app;

void on_message(const std::shared_ptr<vsomeip::message> &_request) {
    std::shared_ptr<vsomeip::payload> its_payload = _request->get_payload();
    vsomeip::length_t l = its_payload->get_length();

    // Get payload
    std::stringstream ss;
    for (vsomeip::length_t i = 0; i < l; i++) {
        ss << std::setw(2) << std::setfill('0') << std::hex
           << (int)*(its_payload->get_data() + i) << " ";
    }

    std::cout << "SERVICE: Received message with Client/Session ["
              << std::setw(4) << std::setfill('0') << std::hex
              << _request->get_client() << "/" << std::setw(4)
              << std::setfill('0') << std::hex << _request->get_session()
              << "] " << ss.str() << std::endl;

    // Create response
    std::shared_ptr<vsomeip::message> its_response =
        vsomeip::runtime::get()->create_response(_request);
    its_payload = vsomeip::runtime::get()->create_payload();
    std::vector<vsomeip::byte_t> its_payload_data;
    for (int i = 9; i >= 0; i--) {
        its_payload_data.push_back(i % 256);
    }
    its_payload->set_data(its_payload_data);
    its_response->set_payload(its_payload);
    app->send(its_response);
}

int main() {
    app = vsomeip::runtime::get()->create_application("World");
    app->init();
    app->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID,
                                  SAMPLE_METHOD_ID, on_message);
    app->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    app->start();
}

客户端程序:

#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
#include <vsomeip/vsomeip.hpp>

#define SAMPLE_SERVICE_ID 0x1234
#define SAMPLE_INSTANCE_ID 0x5678
#define SAMPLE_METHOD_ID 0x0421

std::shared_ptr<vsomeip::application> app;
std::mutex mutex;
std::condition_variable condition;

bool running_ = true;
bool blocked_ = false;
bool is_available_ = false;

void run() {
    std::shared_ptr<vsomeip::message> request;
    request = vsomeip::runtime::get()->create_request(true);
    request->set_service(SAMPLE_SERVICE_ID);
    request->set_instance(SAMPLE_INSTANCE_ID);
    request->set_method(SAMPLE_METHOD_ID);

    std::shared_ptr<vsomeip::payload> its_payload =
        vsomeip::runtime::get()->create_payload();
    std::vector<vsomeip::byte_t> its_payload_data;
    for (vsomeip::byte_t i = 0; i < 10; i++) {
        its_payload_data.push_back(('a' + i) % 256);
    }
    its_payload->set_data(its_payload_data);
    request->set_payload(its_payload);

    while (running_) {
        {
            std::unique_lock<std::mutex> its_lock(mutex);
            while (!blocked_) condition.wait(its_lock);
            if (is_available_) {
                app->send(request);
                std::cout << "Client/Session [" << std::setw(4)
                          << std::setfill('0') << std::hex
                          << request->get_client() << "/" << std::setw(4)
                          << std::setfill('0') << std::hex
                          << request->get_session()
                          << "] sent a request to Service [" << std::setw(4)
                          << std::setfill('0') << std::hex
                          << request->get_service() << "." << std::setw(4)
                          << std::setfill('0') << std::hex
                          << request->get_instance() << "]" << std::endl;
                blocked_ = false;
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
}

void on_message(const std::shared_ptr<vsomeip::message> &_response) {
    std::shared_ptr<vsomeip::payload> its_payload = _response->get_payload();
    vsomeip::length_t l = its_payload->get_length();

    // Get payload
    std::stringstream ss;
    for (vsomeip::length_t i = 0; i < l; i++) {
        ss << std::setw(2) << std::setfill('0') << std::hex
           << (int)*(its_payload->get_data() + i) << " ";
    }

    std::cout << "CLIENT: Received message with Client/Session ["
              << std::setw(4) << std::setfill('0') << std::hex
              << _response->get_client() << "/" << std::setw(4)
              << std::setfill('0') << std::hex << _response->get_session()
              << "] " << ss.str() << std::endl;
}

void send() {
    std::lock_guard<std::mutex> its_lock(mutex);
    blocked_ = true;
    condition.notify_one();
}

void on_availability(vsomeip::service_t _service, vsomeip::instance_t _instance,
                     bool _is_available) {
    std::cout << "CLIENT: Service [" << std::setw(4) << std::setfill('0')
              << std::hex << _service << "." << _instance << "] is "
              << (_is_available ? "available." : "NOT available.") << std::endl;

    if (SAMPLE_SERVICE_ID == _service && SAMPLE_INSTANCE_ID == _instance) {
        if (is_available_ && !_is_available) {
            is_available_ = false;
        } else if (_is_available && !is_available_) {
            is_available_ = true;
            send();
        }
    }
}

int main() {
    app = vsomeip::runtime::get()->create_application("simple-client");
    app->init();
    app->register_availability_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID,
                                       on_availability);
    app->request_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    app->register_message_handler(vsomeip::ANY_SERVICE, SAMPLE_INSTANCE_ID,
                                  vsomeip::ANY_METHOD, on_message);
    std::thread sender(run);
    app->start();
}

Boost Asio 库使用

Asio 是异步 I/O 的重要实现库,以下是 Asio 简单使用示例。

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>

void handler(const boost::system::error_code &ec) {
    std::cout << "[" << std::hex << boost::this_thread::get_id() << "]"
              << __func__ << std::endl;
}

int main() {
    boost::asio::io_service io_service;
    boost::asio::deadline_timer timer(io_service,
                                      boost::posix_time::seconds(5));
    timer.async_wait(handler);
    std::cout << "[" << std::hex << boost::this_thread::get_id() << "]"
              << __func__ << std::endl;
    io_service.run();
}

函数 main() 首先定义了一个 I/O 服务 io_service,用于初始化 I/O 对象 timer。 就像 boost::asio::deadline_timer 那样,所有 I/O 对象通常都需要一个 I/O 服务作为它们的构造函数的第一个参数。 由于 timer 的作用类似于一个闹钟,所以 boost::asio::deadline_timer 的构造函数可以传入第二个参数,用于表示在某个时间点或是在某段时长之后闹钟停止。 以上例子指定了五秒的时长,该闹钟在 timer 被定义之后立即开始计时。async_wait 函数用于启动一个异步 I/O,并指定一个 hanlder 函数,等到五秒时间过去,handler 函数会被 I/O 服务调用。最后需要调用 io_service 的 run 函数,把控制权交给操作系统以接管异步处理,这个函数将阻塞执行,直到所有的 I/O 服务的所有异步操作完成,run 函数才会返回。
Asio 与线程配合使用:

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <mutex>

std::mutex handler_mutex;

void handler(const boost::system::error_code &ec) {
    std::lock_guard<std::mutex> lock(handler_mutex);
    std::cout << "[" << std::hex << boost::this_thread::get_id() << "]"
              << __func__ << std::endl;
}

boost::asio::io_service io_service;

void run() { io_service.run(); }

int main() {
    boost::asio::deadline_timer timer1(io_service,
                                       boost::posix_time::seconds(5));
    timer1.async_wait(handler);
    boost::asio::deadline_timer timer2(io_service,
                                       boost::posix_time::seconds(5));
    timer2.async_wait(handler);
    boost::asio::deadline_timer timer3(io_service,
                                       boost::posix_time::seconds(5));
    timer3.async_wait(handler);
    std::cout << "[" << std::hex << boost::this_thread::get_id() << "]"
              << __func__ << std::endl;
    boost::thread thread1(run);
    boost::thread thread2(run);
    thread1.join();
    thread2.join();
}

运行结果:

[7f0d2414bb80]main
[7f0d229e9700]handler
[7f0d221e8700]handler
[7f0d229e9700]handler

上面的例子 io_service 在两个不同的线程中调用了 run 函数,然后启动 3 个定时器,都定时 5 秒,定时结束后 handler 函数会被调用,在 handler 函数中打印线程 id 和函数名。从运行结果来看 handler 会随机运行在两个不同的线程中。
通过使用多线程,应用程序可以同时调用多个 run() 方法。 一旦某个异步操作结束,相应的 I/O 服务就将在这些线程中的某一个之中执行句柄。 如果第二个操作在第一个操作之后很快也结束了,则 I/O 服务可以在另一个线程中执行句柄,而无需等待第一个句柄终止。
异步 I/O 操作的特点:所有 I/O 操作都交给主线程和内核来处理, 工作线程仅仅负责业务逻辑。
• 主线程:负责监听已连接文件描述符上是否有事件发生。
• 内核:负责处理I/O读写操作。
• 工作线程:仅负责业务逻辑。
在这里插入图片描述
软件框架:
在这里插入图片描述

路由建立

初始化时,创建路由管理者。

bool application_impl::init() {
    ...
        if (is_routing_manager_host_) {
            VSOMEIP_INFO << "Instantiating routing manager [Host].";
            if (client_ == VSOMEIP_CLIENT_UNSET) {
                client_ = static_cast<client_t>(
                          (configuration_->get_diagnosis_address() << 8)
                        & configuration_->get_diagnosis_mask());
                utility::request_client_id(configuration_, name_, client_);
            }
            routing_ = std::make_shared<routing_manager_impl>(this);
        } else {
            VSOMEIP_INFO << "Instantiating routing manager [Proxy].";
            routing_ = std::make_shared<routing_manager_proxy>(this, client_side_logging_, client_side_logging_filter_);
        }

        routing_->set_client(client_);
        routing_->init();
    ...
}

路由分为代理端和主机端,如果没有在配置文件中明确配置路由主机,则第一个启动的应用会自动成为路由主机,其它应用会成为路由代理。代理端应用需要发送消息时,首先将数据通过本地 socket 发送给主机端,再由路由主机通过以太网发送给远程设备。
路由主机实现路由管理者的功能。其初始化函数:

void routing_manager_impl::init() {
    routing_manager_base::init(ep_mgr_impl_);

    // TODO: Only instantiate the stub if needed
    stub_ = std::make_shared<routing_manager_stub>(this, configuration_);
    stub_->init();

    if (configuration_->is_sd_enabled()) {
        VSOMEIP_INFO<< "Service Discovery enabled. Trying to load module.";
        auto its_plugin = plugin_manager::get()->get_plugin(
                plugin_type_e::SD_RUNTIME_PLUGIN, VSOMEIP_SD_LIBRARY);
        if (its_plugin) {
            VSOMEIP_INFO << "Service Discovery module loaded.";
            discovery_ = std::dynamic_pointer_cast<sd::runtime>(its_plugin)->create_service_discovery(this, configuration_);
            discovery_->init();
        } else {
            VSOMEIP_ERROR << "Service Discovery module could not be loaded!";
            std::exit(EXIT_FAILURE);
        }
    }
    ...
}

如果配置文件中使能了 SOMEIP-SD 功能,则加载 SOMEIP-SD 插件库。通过库函数 create_service_discovery 创建 service_discovery 对象,然后调用 service_discovery 的 init 函数,从配置文件中读取 SOMEIP-SD 的通信端口、传输层协议、TTL 等参数。 service_discovery 负责服务发现功能。其配置文件如下:

"service-discovery": {
    "enable": "true",
    "multicast": "224.224.224.245",
    "port": "30490",
    "protocol": "udp",
    "initial_delay_min": "10",
    "initial_delay_max": "100",
    "repetitions_base_delay": "200",
    "repetitions_max": "3",
    "ttl": "3",
    "cyclic_offer_delay": "2000",
    "request_response_delay": "1500"
}

当检测到系统路由就绪则启动服务发现,则开始创建 SOME/IP “路由表”:

void routing_manager_impl::start_ip_routing() {
    ...
    if (discovery_) {
        discovery_->start();
    } else {
        init_routing_info();
    }
    ...
}

如果没有配置服务发现功能,则直接从配置文件导入“路由表”,否则则通过服务发现来创建“路由表”。

void
service_discovery_impl::start() {
    if (!endpoint_) {
        endpoint_ = host_->create_service_discovery_endpoint(
                sd_multicast_, port_, reliable_);
        if (!endpoint_) {
            VSOMEIP_ERROR << "Couldn't start service discovery";
            return;
        }
    }
    ...
    start_main_phase_timer();
    start_offer_debounce_timer(true);
    start_find_debounce_timer(true);
    start_ttl_timer();
}

首先通过 routing_manager_impl 创建通信端点,端点创建成功后通过异步 io 方式等待网络数据到来。然后启动 4 个定时器,他们的功能如下:
• 主阶段定时:周期性发送 Offer 消息。
• Offer 定时器:当有新的服务 Offer 时,首先通过此定时器触发 Offer 消息发送,后面有主阶段定时器周期发送。
• Finder 定时器:当应用需要主动发现服时,通过此服务发送 Find 消息。
• TTL 定时器:更新远程服务的 TTL 时间,TTL 到期的服务,删除相应的路由信息。

发送 Offer 消息

以主阶段定时器为例具体说明:

void
service_discovery_impl::start_main_phase_timer() {
    std::lock_guard<std::mutex> its_lock(main_phase_timer_mutex_);
    boost::system::error_code ec;
    main_phase_timer_.expires_from_now(cyclic_offer_delay_);
    if (ec) {
        VSOMEIP_ERROR<< "service_discovery_impl::start_main_phase_timer "
        "setting expiry time of timer failed: " << ec.message();
    }
    main_phase_timer_.async_wait(
            std::bind(&service_discovery_impl::on_main_phase_timer_expired,
                    this, std::placeholders::_1));
}

定时到期后,on_main_phase_timer_expired 函数会被调用:

void
service_discovery_impl::on_main_phase_timer_expired(
        const boost::system::error_code &_error) {
    if (_error) {
        return;
    }
    send(true);
    start_main_phase_timer();
}

首先通过 send 发送 Offer 消息,然后重启主阶段定时。从而实现周期性发送 Offer 消息。send 函数的实现:

bool
service_discovery_impl::send(bool _is_announcing) {
    std::shared_ptr < runtime > its_runtime = runtime_.lock();
    if (its_runtime) {
        std::vector<std::shared_ptr<message_impl> > its_messages;
        std::shared_ptr<message_impl> its_message;

        if (_is_announcing) {
            its_message = std::make_shared<message_impl>();
            its_messages.push_back(its_message);

            std::lock_guard<std::mutex> its_lock(offer_mutex_);
            services_t its_offers = host_->get_offered_services();
            insert_offer_entries(its_messages, its_offers, true);

            // Serialize and send
            return send(its_messages);
        }
    }
    return false;
}

首先通过 routing_manager_impl 的 get_offered_services 获取所有 Offer 的服务,然后将其填充到 SOMEIP-SD payload 的 entries 字段。最后通过 routing_manager_impl 的 send 发送 Offer 消息。

那么 Offer 的服务信息保存在哪里呢?答案是 routing_manager_impl 的 services_ 成员变量:

services_t services_;

应用程序通过 offer_service 公开服务。

bool routing_manager_base::offer_service(client_t _client,
        service_t _service, instance_t _instance,
        major_version_t _major, minor_version_t _minor) {
    (void)_client;

    ...
        its_info = create_service_info(_service, _instance, _major, _minor,
                DEFAULT_TTL, true);
    ...
    return true;
}

创建服务信息,并保存到 services_ 中。

std::shared_ptr<serviceinfo> routing_manager_base::create_service_info(
        service_t _service, instance_t _instance, major_version_t _major,
        minor_version_t _minor, ttl_t _ttl, bool _is_local_service) {
    std::shared_ptr<serviceinfo> its_info =
            std::make_shared<serviceinfo>(_service, _instance,
                    _major, _minor, _ttl, _is_local_service);
    {
        std::lock_guard<std::mutex> its_lock(services_mutex_);
        services_[_service][_instance] = its_info;
    }
    if (!_is_local_service) {
        std::lock_guard<std::mutex> its_lock(services_remote_mutex_);
        services_remote_[_service][_instance] = its_info;
    }
    return its_info;
}

处理 Offer 消息

端点收到其它设备组播的 Offer 数据包,通过路由后,数据会分发给我 service_discovery 处理,调用其 on_message 函数

void
service_discovery_impl::on_message(
        const byte_t *_data, length_t _length,
        const boost::asio::ip::address &_sender,
        const boost::asio::ip::address &_destination) {
    ...
            if ((*iter)->is_service_entry()) {
                std::shared_ptr<serviceentry_impl> its_service_entry
                    = std::dynamic_pointer_cast<serviceentry_impl>(*iter);
                bool its_unicast_flag = its_message->get_unicast_flag();
                process_serviceentry(its_service_entry, its_options,
                        its_unicast_flag, its_resubscribes,
                        received_via_mcast, accept_state);
            }
    ...
}

对于 Offer 消息,调用 process_offerservice_serviceentry 进一步处理:

void
service_discovery_impl::process_offerservice_serviceentry(
        service_t _service, instance_t _instance, major_version_t _major,
        minor_version_t _minor, ttl_t _ttl,
        const boost::asio::ip::address &_reliable_address,
        uint16_t _reliable_port,
        const boost::asio::ip::address &_unreliable_address,
        uint16_t _unreliable_port,
        std::vector<std::shared_ptr<message_impl> > &_resubscribes,
        bool _received_via_mcast, const sd_acceptance_state_t& _sd_ac_state) {
    ...
    host_->add_routing_info(_service, _instance,
                            _major, _minor,
                            _ttl * get_ttl_factor(_service, _instance, ttl_factor_offers_),
                            _reliable_address, _reliable_port,
                            _unreliable_address, _unreliable_port);
    ...
}

添加路由信息:

void routing_manager_impl::add_routing_info(
        service_t _service, instance_t _instance,
        major_version_t _major, minor_version_t _minor, ttl_t _ttl,
        const boost::asio::ip::address &_reliable_address,
        uint16_t _reliable_port,
        const boost::asio::ip::address &_unreliable_address,
        uint16_t _unreliable_port) {

        ...
        its_info = create_service_info(_service, _instance, _major, _minor, _ttl, is_local);
        init_service_info(_service, _instance, is_local);

            ...
            std::shared_ptr<endpoint_definition> endpoint_def
                = endpoint_definition::get(_unreliable_address, _unreliable_port, false, _service, _instance);
            ep_mgr_impl_->add_remote_service_info(_service, _instance, endpoint_def);
                                    ...
                                    if(!connected) {
                                        ep_mgr_impl_->find_or_create_remote_client(_service, _instance,
                                                false);
                                        connected = true;
                                    }
                                    ...
            ...
            on_availability(_service, _instance, true, _major, _minor);
            stub_->on_offer_service(VSOMEIP_ROUTING_CLIENT, _service, _instance, _major, _minor);
            ...
}

首先为新服务先创建服务信息,将其记录到路由管理者的 services_remote_ 中,然后根据通信协议,创建与之对应的 endpoint_definition,在端点管理器中记录服务服务与端点对应关系,如果应用请求了服务,则会创建根据 endpoint_definition 的记录,创建对应的端点。
消息发送
仅考虑路由主机 Request 消息的情况。应用调用 send 函数,最终会调用到 routing_manager_impl 的 send 函数。

bool routing_manager_impl::send(client_t _client, const byte_t *_data,
        length_t _size, instance_t _instance, bool _reliable,
        client_t _bound_client,
        credentials_t _credentials,
        uint8_t _status_check, bool _sent_from_remote) {
        ...
                if (is_request) {
                    its_target = ep_mgr_impl_->find_or_create_remote_client(
                            its_service, _instance, _reliable);
                    if (its_target) {
                        is_sent = its_target->send(_data, _size);
                    }
    ...
}

通过端点管理找到服务对应的端点,使用端点的 send 函数发送。

消息接收

首先端点都是异步接收网络数据的,已 udp client 为例:

void udp_client_endpoint_impl::receive() {
    std::lock_guard<std::mutex> its_lock(socket_mutex_);
    if (!socket_->is_open()) {
        return;
    }
    message_buffer_ptr_t its_buffer = std::make_shared<message_buffer_t>(VSOMEIP_MAX_UDP_MESSAGE_SIZE);
    socket_->async_receive_from(
        boost::asio::buffer(*its_buffer),
        const_cast<endpoint_type&>(remote_),
        strand_.wrap(
            std::bind(
                &udp_client_endpoint_impl::receive_cbk,
                std::dynamic_pointer_cast<
                    udp_client_endpoint_impl
                >(shared_from_this()),
                std::placeholders::_1,
                std::placeholders::_2,
                its_buffer
            )
        )
    );
}

当接到消息后,会异步调用 receive_cbk 函数处理消息。

void udp_client_endpoint_impl::receive_cbk(
        boost::system::error_code const &_error, std::size_t _bytes,
        const message_buffer_ptr_t& _recv_buffer) {
        ...
                                its_host->on_message(&(*_recv_buffer)[i],
                                             VSOMEIP_SOMEIP_HEADER_SIZE + 8, this,
                                             boost::asio::ip::address(),
                                             VSOMEIP_ROUTING_CLIENT,
                                             std::make_pair(ANY_UID, ANY_GID),
                                             remote_address_,
                                             remote_port_);
        ...
}

最后会将消息交给路由管理者处理:

bool routing_manager_impl::on_message(
        service_t _service, instance_t _instance,
        const byte_t *_data, length_t _size,
        bool _reliable, client_t _bound_client,
        credentials_t _credentials,
        uint8_t _check_status,
        bool _is_from_remote) {
   ...
        deliver_message(_data, _size, _instance,
    ...
}

消息投递:

bool routing_manager_impl::deliver_message(const byte_t *_data, length_t _size,
        instance_t _instance, bool _reliable, client_t _bound_client, credentials_t _credentials,
        uint8_t _status_check, bool _is_from_remote) {
        ...
        host_->on_message(std::move(its_message));
        ...
}

这里 host_ 是 application_impl 的实例。其 on_message 函数如下:

void application_impl::on_message(std::shared_ptr<message> &&_message) {
    ...
    {
        std::lock_guard<std::mutex> its_lock(members_mutex_);
        std::set<message_handler> its_handlers;
        auto found_service = members_.find(its_service);
        if (found_service != members_.end()) {
            auto found_instance = found_service->second.find(its_instance);
            if (found_instance != found_service->second.end()) {
                auto found_method = found_instance->second.find(its_method);
                if (found_method != found_instance->second.end()) {
                    its_handlers.insert(found_method->second);
                }
                ...
            }
            ...
        }
        ...

        if (its_handlers.size()) {
            std::lock_guard<std::mutex> its_lock(handlers_mutex_);
            for (const auto &its_handler : its_handlers) {
                auto handler = its_handler.handler_;
                std::shared_ptr<sync_handler> its_sync_handler =
                        std::make_shared<sync_handler>([handler, _message]() {
                            handler(_message);
                        });
                its_sync_handler->handler_type_ = handler_type_e::MESSAGE;
                its_sync_handler->service_id_ = _message->get_service();
                its_sync_handler->instance_id_ = _message->get_instance();
                its_sync_handler->method_id_ = _message->get_method();
                its_sync_handler->session_id_ = _message->get_session();
                handlers_.push_back(its_sync_handler);
            }
            dispatcher_condition_.notify_one();
        }
    }
}

注册过消息回调的服务都保存在 members_ 中。从 members_ 找到匹配消息头部的的回到,规整到 handlers_ 集合中。然后为每一个回调创建一个 sync_handler,并加入到全局的 handlers_ 列表中。最后唤醒消息分发线程。

消息分发

application_impl 启动:
在这里插入图片描述
消息分发器创建:

void application_impl::start() {
    ...
        start_caller_id_ = std::this_thread::get_id();
        {
            std::lock_guard<std::mutex> its_lock(dispatcher_mutex_);
            is_dispatching_ = true;
            auto its_main_dispatcher = std::make_shared<std::thread>(
                    std::bind(&application_impl::main_dispatch, shared_from_this()));
            dispatchers_[its_main_dispatcher->get_id()] = its_main_dispatcher;
        }
        ...
        io_.run();
        ...
}

主分发线程:

void application_impl::main_dispatch() {
    ...
    const std::thread::id its_id = std::this_thread::get_id();
    ...
    std::unique_lock<std::mutex> its_lock(handlers_mutex_);
    while (is_dispatching_) {
        if (handlers_.empty() || !is_active_dispatcher(its_id)) {
            // Cancel other waiting dispatcher
            dispatcher_condition_.notify_all();
            // Wait for new handlers to execute
            while (is_dispatching_ && (handlers_.empty() || !is_active_dispatcher(its_id))) {
                dispatcher_condition_.wait(its_lock);
            }
        } else {
            std::shared_ptr<sync_handler> its_handler;
            while (is_dispatching_  && is_active_dispatcher(its_id)
                   && (its_handler = get_next_handler())) {
                its_lock.unlock();
                invoke_handler(its_handler);

                if (!is_dispatching_)
                    return;

                its_lock.lock();

                reschedule_availability_handler(its_handler);
                remove_elapsed_dispatchers();
            }
        }
    }
    its_lock.unlock();
}

handlers_ 是一个队列,当收到 someip 消息后,将消息的内容和处理行数将封装成 sync_handler 放到 handlers_ 队列:

mutable std::deque<std::shared_ptr<sync_handler>> handlers_;

当主分发线程被唤醒,并且 handlers_ 不为空时,调用 invoke_handler 依次出来队列中的 sync_handler。

void application_impl::invoke_handler(std::shared_ptr<sync_handler> &_handler) {
    const std::thread::id its_id = std::this_thread::get_id();

    std::shared_ptr<sync_handler> its_sync_handler
        = std::make_shared<sync_handler>(_handler->service_id_,
            _handler->instance_id_, _handler->method_id_,
            _handler->session_id_, _handler->eventgroup_id_,
            _handler->handler_type_);

    boost::asio::steady_timer its_dispatcher_timer(io_);
    its_dispatcher_timer.expires_from_now(std::chrono::milliseconds(max_dispatch_time_));
    its_dispatcher_timer.async_wait([this, its_sync_handler](const boost::system::error_code &_error) {
        if (!_error) {
            print_blocking_call(its_sync_handler);
            if (has_active_dispatcher()) {
                std::lock_guard<std::mutex> its_lock(handlers_mutex_);
                dispatcher_condition_.notify_all();
            } else {
                // If possible, create a new dispatcher thread to unblock.
                // If this is _not_ possible, dispatching is blocked until
                // at least one of the active handler calls returns.
                while (is_dispatching_) {
                    if (dispatcher_mutex_.try_lock()) {
                        if (dispatchers_.size() < max_dispatchers_) {
                            if (is_dispatching_) {
                                auto its_dispatcher = std::make_shared<std::thread>(
                                    std::bind(&application_impl::dispatch, shared_from_this()));
                                dispatchers_[its_dispatcher->get_id()] = its_dispatcher;
                            } else {
                                VSOMEIP_INFO << "Won't start new dispatcher "
                                        "thread as Client=" << std::hex
                                        << get_client() << " is shutting down";
                            }
                        } else {
                            VSOMEIP_ERROR << "Maximum number of dispatchers exceeded.";
                        }
                        dispatcher_mutex_.unlock();
                        break;
                    } else {
                        std::this_thread::yield();
                    }
                }
            }
        }
    });
    ...

    if (is_dispatching_) {
        try {
            _handler->handler_();
        } catch (const std::exception &e) {
            VSOMEIP_ERROR << "application_impl::invoke_handler caught exception: "
                    << e.what();
            print_blocking_call(its_sync_handler);
        }
    }
    ...
}

首先启动 its_dispatcher_timer 定时,用于检查消息处理是否超时,如果超时则启动新的 dispatch 处理后续消息。
然后调用 sync_handler 的 handler_ 函数。最会调用应用程序注册的回调函数。

https://github.com/COVESA/vsomeip/wiki/vsomeip-in-10-minutes

  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翻滚吧香香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值