LCM中間件入門(2):LCM核心實現原理解析

文章目錄

        • 一、`good()`函數:LCM實例狀態檢查的實現原理
          • 1. 實現邏輯
          • 2. 簡化代碼示例(C語言核心邏輯)
        • 二、`publish()`:向指定channel發送消息的原理
          • 1. 完整流程拆解
          • 2. 簡化代碼示例(C++核心邏輯)
        • 三、`subscribe()`:接收指定channel消息的原理
          • 1. 完整流程拆解
          • 2. 簡化代碼示例(C++核心邏輯)
      • 四、整體協同機制總結

LCM的核心功能基于C語言實現(C++接口為其封裝),其底層通過 UDP網絡通信消息序列化回調管理實現發布-訂閱模式。以下從代碼原理層面解析關鍵函數的工作機制。

一、good()函數:LCM實例狀態檢查的實現原理

good()函數用于判斷LCM實例是否初始化成功,其核心是檢查LCM內部關鍵資源的有效性。

1. 實現邏輯

LCM實例(lcm_t結構體)的核心成員包括:

  • 網絡套接字(socket_fd):用于收發數據的UDP套接字;
  • 線程狀態(thread_running):接收消息的后臺線程是否啟動;
  • 錯誤碼(error):記錄初始化或運行中的錯誤狀態。

good()函數的本質是檢查這些成員是否處于“可用狀態”:

  • 套接字是否成功創建(socket_fd != -1);
  • 后臺線程是否正常運行(針對需要異步接收的模式);
  • 無致命錯誤(error == 0)。
2. 簡化代碼示例(C語言核心邏輯)
// LCM實例結構體(簡化)
typedef struct {int socket_fd;          // UDP套接字描述符int thread_running;     // 接收線程狀態(1=運行,0=停止)int error;              // 錯誤碼(0=無錯誤)// 其他成員:回調表、組播地址等
} lcm_t;// good()函數實現
int lcm_good(lcm_t *lcm) {return (lcm != NULL && lcm->socket_fd != -1 && lcm->thread_running && lcm->error == 0);
}

在C++接口中,lcm::LCM::good()是對上述C函數的封裝,返回bool類型。

二、publish():向指定channel發送消息的原理

publish()的核心是將消息序列化為字節流,并通過UDP組播發送到與channel關聯的網絡地址,同時在數據包中嵌入channel標識。

1. 完整流程拆解
  1. 消息序列化
    根據.lcm文件生成的編解碼函數(如example_temperature_t_pack()),將消息結構體轉換為二進制字節流(解決跨平臺數據格式差異)。

  2. channel與網絡地址映射
    LCM默認將channel名稱映射為UDP組播地址(239.255.x.y,其中x.y由channel名稱的哈希值計算得出),確保同一channel的消息僅被訂閱該channel的節點接收。

  3. 數據包封裝
    構造LCM協議數據包,格式為:

    [4字節魔數] + [4字節消息長度] + [channel名稱] + [序列化的消息數據]
    

    魔數(0x4C434D00,即"LCM\0")用于接收方識別LCM數據包。

  4. UDP發送
    通過LCM實例的套接字將數據包發送到channel對應的組播地址。

2. 簡化代碼示例(C++核心邏輯)
// C++ publish()接口
void LCM::publish(const std::string& channel, const void* data, size_t len) {if (!good()) return;  // 檢查實例狀態// 1. 計算channel對應的組播地址(基于哈希)struct sockaddr_in addr;lcm_channel_to_multicast(channel.c_str(), &addr);  // 內部哈希映射// 2. 封裝LCM協議頭uint8_t header[8];header[0] = 0x4C; header[1] = 0x43; header[2] = 0x4D; header[3] = 0x00;  // 魔數*(uint32_t*)(header + 4) = htonl(len);  // 消息長度(網絡字節序)// 3. 拼接完整數據包:頭 + channel + 消息數據std::vector<uint8_t> packet;packet.insert(packet.end(), header, header + 8);packet.insert(packet.end(), channel.begin(), channel.end());packet.push_back('\0');  // channel以空字符結尾packet.insert(packet.end(), (uint8_t*)data, (uint8_t*)data + len);// 4. 通過UDP發送到組播地址sendto(lcm->socket_fd, packet.data(), packet.size(), 0,(struct sockaddr*)&addr, sizeof(addr));
}
三、subscribe():接收指定channel消息的原理

subscribe()的核心是注冊回調函數并與channel綁定,后臺線程接收數據包后,根據channel查找對應的回調并觸發執行。

1. 完整流程拆解
  1. 回調函數注冊
    訂閱時,LCM將channel名稱消息類型回調函數存儲在內部的回調表(哈希表,channel -> 回調列表)中。

  2. 后臺接收線程
    LCM初始化時啟動一個后臺線程,循環從套接字讀取UDP數據包:

    • 解析數據包,驗證魔數和格式;
    • 提取channel名稱和序列化的消息數據。
  3. 消息路由與反序列化
    根據解析出的channel名稱,在回調表中查找對應的回調函數:

    • 若找到,調用自動生成的反序列化函數(如example_temperature_t_unpack()),將字節流轉換為消息結構體;
    • 調用注冊的回調函數,傳入消息結構體。
  4. 線程安全處理
    回調函數的執行在后臺線程中進行,若需在多線程環境中使用,需用戶自行添加同步機制(如互斥鎖)。

2. 簡化代碼示例(C++核心邏輯)
// 回調表結構(簡化):channel -> 回調函數列表
typedef struct {std::unordered_map<std::string, std::vector<Callback>> callbacks;std::mutex mutex;  // 保護回調表的線程安全
} CallbackTable;// 訂閱函數實現
void LCM::subscribe(const std::string& channel, void (*callback)(const ReceiveBuffer*, const std::string&, void*),void* userdata) {std::lock_guard<std::mutex> lock(callback_table.mutex);// 將回調函數注冊到channel對應的列表中callback_table.callbacks[channel].emplace_back(callback, userdata);
}// 后臺接收線程邏輯
void receive_thread(lcm_t* lcm) {while (lcm->thread_running) {uint8_t buffer[65536];  // UDP最大包長struct sockaddr_in sender;socklen_t sender_len = sizeof(sender);ssize_t n = recvfrom(lcm->socket_fd, buffer, sizeof(buffer), 0,(struct sockaddr*)&sender, &sender_len);if (n <= 0) continue;// 解析數據包:檢查魔數、提取channel和消息數據if (buffer[0] != 0x4C || buffer[1] != 0x43 || buffer[2] != 0x4D || buffer[3] != 0x00)continue;  // 非LCM數據包,忽略uint32_t msg_len = ntohl(*(uint32_t*)(buffer + 4));std::string channel = (char*)(buffer + 8);  // channel以空字符結尾const uint8_t* msg_data = buffer + 8 + channel.size() + 1;// 查找回調并執行std::lock_guard<std::mutex> lock(callback_table.mutex);auto it = callback_table.callbacks.find(channel);if (it != callback_table.callbacks.end()) {for (auto& cb : it->second) {// 構造接收緩沖區,調用回調ReceiveBuffer rbuf{msg_data, msg_len};cb.function(&rbuf, channel, cb.userdata);}}}
}

四、整體協同機制總結

工作原理交互圖如下:

PublisherLCM_Pub (發布者實例)Network (UDP組播)LCM_Sub (訂閱者實例)Subscriber初始化LCM實例創建lcm_t結構體1. 創建UDP套接字2. 啟動接收線程3. 初始化錯誤碼為0調用good()返回狀態 (socket有效+線程運行+無錯誤)初始化LCM實例創建lcm_t結構體1. 創建UDP套接字2. 啟動接收線程3. 初始化回調表(空)調用good()返回狀態 (socket有效+線程運行+無錯誤)訂閱通道THERMOMETER調用subscribe("THERMOMETER", 回調函數)1. 加鎖保護回調表2. 將回調函數注冊到"THERMOMETER"條目下訂閱完成發布消息到通道構造Temperature消息(溫度值、時間戳等)調用publish("THERMOMETER", 消息)publish()內部處理1. 消息序列化(調用自動生成的pack函數)2. 計算channel哈希映射到組播地址(239.255.x.y)3. 封裝LCM數據包(魔數+長度+channel+序列化數據)通過UDP發送數據包到組播地址接收UDP數據包(含"THERMOMETER"標識)接收線程處理1. 驗證魔數和數據包格式2. 提取channel("THERMOMETER")和序列化數據3. 消息反序列化(調用自動生成的unpack函數)4. 查找回調表中"THERMOMETER"對應的回調函數觸發回調函數(傳入反序列化后的消息)執行回調邏輯(打印溫度數據等)PublisherLCM_Pub (發布者實例)Network (UDP組播)LCM_Sub (訂閱者實例)Subscriber
  1. 初始化階段lcm_t實例創建套接字、啟動接收線程,`good(
  2. )`驗證這些資源是否就緒。
  3. 發布階段publish()將消息序列化,通過channel映射的組播地址發送UDP包,嵌入channel標識。
  4. 訂閱階段subscribe()將回調注冊到channel對應的哈希表;接收線程解析UDP包,根據channel查找回調,反序列化消息后觸發執行。

這種設計實現了無中心節點的輕量化通信,通過UDP組播和哈希映射保證低延遲,適用于實時系統中基于channel的高效數據交互。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/91893.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/91893.shtml
英文地址,請注明出處:http://en.pswp.cn/web/91893.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Nginx安裝及配置

一.nginx安裝1.1nginx概述1.1.1 nginx介紹Nginx是一款高性能的開源HTTP和反向代理服務器&#xff0c;是免費的、開源的、高性能的HTTP和反向代理服務器、郵件代理服務器、以及TCP/UDP代理服務器解決C10K問題&#xff08;10K Connections&#xff09;。同時也支持IMAP/POP3代理服…

SelectDB數據庫,新一代實時數據倉庫的全面解析與應用

摘要&#xff1a;SelectDB是一款基于Apache Doris的新一代實時數據倉庫解決方案&#xff0c;具備實時極速、融合統一、彈性架構和開放生態四大核心特性。它采用云原生存算分離架構&#xff0c;支持秒級數據更新、毫秒級查詢響應&#xff0c;在TPC-H等基準測試中性能超越傳統系統…

自動駕駛的未來:多模態傳感器鉆機

倫敦大學學院博士生袁方正在建造多模態傳感器鉆機&#xff0c;以探索自動駕駛的未來。他的最新設置匯集了一套尖端傳感器&#xff1a; &#x1f4e1; 60 GHz 雷達&#xff08;用于 Raspberry Pi 的 DreamHAT&#xff09;DreamRF &#x1f4f7; RGB 深度攝像頭 &#xff08;Real…

13.Redis 的級聯復制

Redis 的級聯復制 即實現基于Slave節點的Slave 1. 修改 Slave 節點配置文件 # 第一個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/redis.conf(大約在533行附近) replicaof 10.0.0.100 6379 masterauth 123456# 第二個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/…

spring-ai-alibaba 學習(二十)——graph之檢查點

前面學習了graph的基本概念&#xff0c;參數設置&#xff0c;特殊節點和邊&#xff0c;今天學習一下檢查點檢查點可能名稱比較抽象&#xff0c;換個名字可能比較容易理解&#xff0c;進度保存點或者存檔點&#xff0c;可以類比游戲中保存當前游戲進度的存檔進度主要用于人工介入…

sqli-labs:Less-19關卡詳細解析

1. 思路&#x1f680; 本關的SQL語句為&#xff1a; $insert"INSERT INTO security.referers (referer, ip_address) VALUES ($uagent, $IP)";注入類型&#xff1a;字符串型&#xff08;單引號包裹&#xff09;、INSERT操作提示&#xff1a;參數需以閉合關鍵參數&a…

Java小紅書源碼1:1還原uniapp_仿小紅書源碼

在內容驅動型社交平臺興起的背景下&#xff0c;小紅書作為圖文/視頻種草社區的代表&#xff0c;其產品結構與功能體驗逐漸成為眾多開發者與創業團隊的模仿藍本。本項目基于Java后端uni-app前端棧&#xff0c;完整復刻小紅書主要功能&#xff0c;支持多端&#xff08;小程序、H5…

USB Type-C PD協議一文通

原文&#xff1a;https://www.richtek.com/Design%20Support/Technical%20Document/AN056?sc_langzh-TW譯者&#xff1a;TrustZone1、概述 USB Type-C標準的出現是為了滿足不斷增長的現代設備之間的連接需要&#xff0c;它在傳統USB標準的基礎上提供了更高的電源傳輸能力和資料…

AI文檔比對和Word的“比較”功能有什么區別?

AI文檔比對工具的核心區別在于&#xff0c;它超越了Word的純文本“找不同”&#xff0c;能精準處理掃描件、表格及印章&#xff0c;并將文檔審查從被動的文本核對&#xff0c;處理大文檔也更為快速及準確。 為什么Word的“比較”功能已經不夠用了&#xff1f; 對于許多專業人士…

AI驅動SEO關鍵詞智能進化

內容概要 隨著人工智能&#xff08;AI&#xff09;技術的快速演進&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;領域正迎來前所未有的變革。本文核心探討AI如何驅動SEO關鍵詞的智能進化&#xff0c;重點解析人工智能革新關鍵詞研究與優化策略的機制&#xff0c;包括智能…

基于SpringBoot+MyBatis+MySQL+VUE實現的青年公寓服務平臺管理系統(附源碼+數據庫+畢業論文+部署教程+配套軟件)

摘 要 傳統信息的管理大部分依賴于管理人員的手工登記與管理&#xff0c;然而&#xff0c;隨著近些年信息技術的迅猛發展&#xff0c;讓許多比較老套的信息管理模式進行了更新迭代&#xff0c;房屋信息因為其管理內容繁雜&#xff0c;管理數量繁多導致手工進行處理不能滿足廣…

12.Redis 主從復制

Redis 主從復制Redis 主從復制1. Redis 主從復制架構2. 主從復制實現2.1 主從命令配置2.1.1 啟用主從同步2.1.2 查看日志觀察同步狀態2.1.3 修改 Slave 節點配置文件2.1.4 刪除主從同步3. 主從復制故障恢復3.1 Slave 節點故障和恢復3.2 Master 節點故障和恢復3.3 常見主從復制故…

微服務的編程測評系統8-題庫管理-競賽管理

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄前言1. 添加題目1.1 service方法1.2 畫頁面-引入富文本和代碼編輯框1.3 子組件中發送請求2. 獲取題目詳情3. 編輯題目4. 刪除題目5. Vue生命周期函數5.1 創建階段5.2…

基于springboot的學習輔導系統設計與實現

學生&#xff1a;注冊登錄&#xff0c;學習視頻&#xff0c;學習資料&#xff0c;在線交流&#xff0c;系統公告&#xff0c;個人中心&#xff0c;后臺管理教師&#xff1a;登錄&#xff0c;個人中心&#xff0c;學習視頻管理&#xff0c;學習資料管理&#xff0c;簽到記錄管理…

Kubernetes (K8s) 部署Doris

官網提供yaml地址下載部署 https://doris.apache.org/zh-CN/docs/2.0/install/cluster-deployment/k8s-deploy/install-env/禁用和關閉 swap 在部署 Doris 時&#xff0c;建議關閉 swap 分區。 通過以下命令可以永久關閉 swap 分區。 echo "vm.swappiness 0">>…

AI生成圖片工具分享!

CZL在線工具箱近日推出了一款基于Cloudflare Workers AI的免費在線AI圖片生成服務。該服務采用**Stable Diffusion XL&#xff08;SDXL&#xff09;**模型&#xff0c;為用戶提供高質量、逼真的圖像生成體驗。 核心特性 全球GPU網絡&#xff1a;基于Cloudflare全球分布式GPU網…

Spring Batch的2種STEP定義方式

Spring Batch的2種STEP定義方式 1. 第一種&#xff1a;基于Chunk-Oriented Processing&#xff08;read&#xff0c;process&#xff0c;write&#xff09;形式 適用場景&#xff1a; 大數據量批處理&#xff1a;適合需要分批次讀取、處理并寫入大量數據的場景&#xff08;如數…

前端JS-調用單刪接口來刪除多個選中文件

當開發中遇到&#xff1a;服務端沒有刪除多個文件功能接口&#xff0c;只有單個刪除文件功能接口時&#xff0c;會遇到如何多選刪除文件效果最佳。await Promise.all(selectedDocPaths.map(async (path) > {try {await fileDelete(path)} catch (err) {throw new Error(刪除…

機器學習——過采樣(OverSampling),解決類別不平衡問題,案例:邏輯回歸 信用卡欺詐檢測

下采樣&#xff1a;機器學習——下采樣&#xff08;UnderSampling&#xff09;&#xff0c;解決類別不平衡問題&#xff0c;案例&#xff1a;邏輯回歸 信用卡欺詐檢測-CSDN博客 &#xff08;完整代碼在底部&#xff09; 解決樣本不平衡問題&#xff1a;SMOTE 過采樣實戰講解 …

Ettus USRP X440 進行“超短波個人衛星信號的偵查與干擾”任務

結合 Ettus USRP X440 進行“超短波個人衛星信號的偵查與干擾”任務&#xff0c;可以構建一個高性能、靈活可編程的電子對抗系統原型平臺。以下是面向科研/工程/軍用驗證場景的構思和技術文案&#xff1a; &#x1f6f0;? 項目名稱建議&#xff08;可選&#xff09;&#xff1…