【實時Linux實戰系列】實時數據流的網絡傳輸

在實時系統中,數據流的實時傳輸是許多應用場景的核心需求之一。無論是工業自動化中的傳感器數據、金融交易中的高頻數據,還是多媒體應用中的視頻流,都需要在嚴格的時間約束內完成數據的傳輸。實時數據流的傳輸不僅要求高吞吐量,還要求低延遲和高可靠性。掌握實時數據流的網絡傳輸技能對于開發者來說至關重要,它可以幫助開發者設計出更加高效和可靠的實時系統。

本文將通過實際案例,詳細介紹如何在實時 Linux 下實現數據流的實時傳輸,包括優化網絡傳輸延遲的技術手段及方案。我們將從基本的網絡傳輸概念入手,逐步深入到具體的實現細節和優化方法。

核心概念

1. 實時數據流

實時數據流是指需要在嚴格的時間約束內傳輸的數據序列。實時數據流的特性包括:

  • 時間敏感性:數據必須在規定的時間內到達目的地,否則可能失去價值。

  • 高吞吐量:數據流通常包含大量數據,需要高效的傳輸機制。

  • 可靠性:數據傳輸過程中需要保證數據的完整性和準確性。

2. 網絡傳輸延遲

網絡傳輸延遲是指數據從發送端到接收端所需的時間。延遲的來源包括:

  • 處理延遲:數據在發送端和接收端的處理時間。

  • 傳輸延遲:數據在物理介質中的傳輸時間。

  • 排隊延遲:數據在網絡節點(如路由器、交換機)中的排隊時間。

  • 傳播延遲:信號在物理介質中的傳播時間。

3. 實時 Linux

實時 Linux 是一種經過優化的 Linux 系統,能夠提供低延遲和高確定性的任務調度。它通過實時補丁(如 PREEMPT_RT)來增強 Linux 內核的實時性,適用于需要高實時性的應用場景。

4. 傳輸協議

傳輸協議是數據在網絡中傳輸的規則。常見的傳輸協議包括:

  • TCP(傳輸控制協議):提供可靠的、面向連接的傳輸服務,適用于對可靠性要求較高的場景。

  • UDP(用戶數據報協議):提供無連接的、不可靠的傳輸服務,適用于對實時性要求較高的場景。

  • RTP(實時傳輸協議):專門用于實時數據流的傳輸,支持數據的時間戳和序列號,適用于多媒體應用。

環境準備

1. 操作系統

  • 推薦系統:Ubuntu 20.04 或更高版本(建議使用實時內核,如 PREEMPT_RT)。

  • 安裝實時內核

    1. 添加實時內核 PPA:

    2. sudo add-apt-repository ppa:longsleep/golang-backports
      sudo add-apt-repository ppa:ubuntu-toolchain-r/test
      sudo add-apt-repository ppa:realtime-linux/ppa
      sudo apt update
    3. 安裝實時內核:

    4. sudo apt install linux-image-rt-amd64
    5. 重啟系統并選擇實時內核啟動。

2. 開發工具

  • 推薦工具gcc(用于編譯 C 程序)。

  • 安裝方法

  • sudo apt update
    sudo apt install build-essential

3. 測試工具

  • 推薦工具iperf3(用于測試網絡帶寬和延遲)。

  • 安裝方法

  • sudo apt install iperf3

實際案例與步驟

1. 使用 UDP 實現實時數據流傳輸

示例代碼

以下代碼展示了如何使用 UDP 實現實時數據流的傳輸。UDP 是一種無連接的協議,適用于對實時性要求較高的場景。

// sender.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr;// 創建 UDP 套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");char buffer[BUFFER_SIZE];while (1) {printf("Enter data to send: ");fgets(buffer, BUFFER_SIZE, stdin);// 發送數據sendto(sockfd, buffer, strlen(buffer), 0, (const struct sockaddr*)&servaddr, sizeof(servaddr));}close(sockfd);return 0;
}

?

// receiver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr, cliaddr;// 創建 UDP 套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);// 綁定套接字if (bind(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("socket bind failed");exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];int n;socklen_t len = sizeof(cliaddr);while (1) {// 接收數據n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&cliaddr, &len);buffer[n] = '\0';printf("Received data: %s\n", buffer);}close(sockfd);return 0;
}
編譯與運行
  1. 編譯代碼:

  2. gcc -o sender sender.c
    gcc -o receiver receiver.c
  3. 運行程序:

    • 在一個終端運行接收端:

  4. ./receiver
  5. 在另一個終端運行發送端:

  6. ./sender
    代碼說明
    • UDP 套接字:使用 socket 函數創建 UDP 套接字。

    • 發送數據:使用 sendto 函數發送數據。

    • 接收數據:使用 recvfrom 函數接收數據。

    • 服務器地址:配置服務器的 IP 地址和端口號。

    2. 使用 TCP 實現實時數據流傳輸

    示例代碼

    以下代碼展示了如何使用 TCP 實現實時數據流的傳輸。TCP 是一種面向連接的協議,適用于對可靠性要求較高的場景。

    c

    復制

    // tcp_server.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>#define PORT 8080
    #define BUFFER_SIZE 1024int main() {int sockfd, connfd;struct sockaddr_in servaddr, cliaddr;// 創建 TCP 套接字if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);// 綁定套接字if (bind(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("socket bind failed");exit(EXIT_FAILURE);}// 監聽連接if (listen(sockfd, 5) < 0) {perror("socket listen failed");exit(EXIT_FAILURE);}int len = sizeof(cliaddr);if ((connfd = accept(sockfd, (struct sockaddr*)&cliaddr, &len)) < 0) {perror("socket accept failed");exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];int n;while (1) {// 接收數據n = read(connfd, buffer, BUFFER_SIZE);buffer[n] = '\0';printf("Received data: %s\n", buffer);// 發送數據write(connfd, buffer, strlen(buffer));}close(sockfd);close(connfd);return 0;
    }

    ?

    // tcp_client.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>#define PORT 8080
    #define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr;// 創建 TCP 套接字if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");// 連接到服務器if (connect(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("socket connection failed");exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];while (1) {printf("Enter data to send: ");fgets(buffer, BUFFER_SIZE, stdin);// 發送數據write(sockfd, buffer, strlen(buffer));// 接收數據read(sockfd, buffer, BUFFER_SIZE);printf("Received data: %s", buffer);}close(sockfd);return 0;
    }
    編譯與運行
    1. 編譯代碼:

    2. gcc -o tcp_server tcp_server.c
      gcc -o tcp_client tcp_client.c
    3. 運行程序:

      • 在一個終端運行服務器端:

    4. ./tcp_server
    5. 在另一個終端運行客戶端:

    6. ./tcp_client
      代碼說明
      • TCP 套接字:使用 socket 函數創建 TCP 套接字。

      • 連接:使用 connect 函數連接到服務器。

      • 發送數據:使用 write 函數發送數據。

      • 接收數據:使用 read 函數接收數據。

      • 服務器地址:配置服務器的 IP 地址和端口號。

      3. 使用 RTP 實現實時數據流傳輸

      示例代碼

      以下代碼展示了如何使用 RTP 實現實時數據流的傳輸。RTP 是一種專門用于實時數據流的傳輸協議,支持數據的時間戳和序列號。

      // rtp_sender.c
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <unistd.h>
      #include <arpa/inet.h>#define PORT 8080
      #define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr;// 創建 UDP 套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_port = htons(PORT);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");char buffer[BUFFER_SIZE];int seq_num = 0;while (1) {printf("Enter data to send: ");fgets(buffer, BUFFER_SIZE, stdin);// 添加 RTP 頭部buffer[0] = (seq_num >> 8) & 0xFF; // 序列號高字節buffer[1] = seq_num & 0xFF;       // 序列號低字節seq_num++;// 發送數據sendto(sockfd, buffer, strlen(buffer) + 2, 0, (const struct sockaddr*)&servaddr, sizeof(servaddr));}close(sockfd);return 0;
      }

      ?

      // rtp_receiver.c
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <unistd.h>
      #include <arpa/inet.h>#define PORT 8080
      #define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in servaddr, cliaddr;// 創建 UDP 套接字if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket creation failed");exit(EXIT_FAILURE);}memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));// 配置服務器地址servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);// 綁定套接字if (bind(sockfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {perror("socket bind failed");exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];int n;socklen_t len = sizeof(cliaddr);while (1) {// 接收數據n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&cliaddr, &len);buffer[n] = '\0';// 提取 RTP 頭部int seq_num = (buffer[0] << 8) | buffer[1];printf("Received data (seq_num: %d): %s\n", seq_num, buffer + 2);}close(sockfd);return 0;
      }
      編譯與運行
      1. 編譯代碼:

      2. gcc -o rtp_sender rtp_sender.c
        gcc -o rtp_receiver rtp_receiver.c
      3. 運行程序:

        • 在一個終端運行接收端:

      4. ./rtp_receiver
      5. 在另一個終端運行發送端:

      6. ./rtp_sender
        代碼說明
        • RTP 頭部:在數據前添加序列號,用于標識數據包的順序。

        • UDP 套接字:使用 socket 函數創建 UDP 套接字。

        • 發送數據:使用 sendto 函數發送數據。

        • 接收數據:使用 recvfrom 函數接收數據。

        • 服務器地址:配置服務器的 IP 地址和端口號。

        常見問題與解答

        1. 如何選擇合適的傳輸協議?

        選擇合適的傳輸協議取決于具體的應用場景:

        • UDP:適用于對實時性要求較高的場景,如視頻流、音頻流。

        • TCP:適用于對可靠性要求較高的場景,如文件傳輸、數據庫通信。

        • RTP:適用于需要時間戳和序列號的實時數據流,如多媒體應用。

        2. 如何優化網絡傳輸延遲?

        可以通過以下方法優化網絡傳輸延遲:

        • 使用 UDP:UDP 是一種無連接的協議,傳輸延遲較低。

        • 減少數據包大小:較小的數據包可以減少傳輸延遲。

        • 使用實時 Linux:實時 Linux 可以提供低延遲和高確定性的任務調度。

        • 優化網絡配置:調整網絡參數,如 MTU(最大傳輸單元)和緩沖區大小。

        3. 如何調試網絡傳輸問題?

        可以通過以下方法調試網絡傳輸問題:

        • 使用 iperf3:測試網絡帶寬和延遲。

        • 使用 tcpdump:捕獲和分析網絡數據包。

        • 使用 netstat:查看網絡連接和端口狀態。

        4. 如何處理數據包丟失?

        可以通過以下方法處理數據包丟失:

        • 使用 TCP:TCP 提供可靠的數據傳輸,可以自動處理數據包丟失。

        • 實現重傳機制:在 UDP 中實現自定義的重傳機制。

        • 使用 FEC(前向糾錯):在數據中添加冗余信息,以便在數據包丟失時恢復數據。

        實踐建議與最佳實踐

        1. 合理選擇傳輸協議

        根據具體的應用場景選擇合適的傳輸協議,避免過度優化導致復雜性增加。

        2. 使用實時 Linux

        實時 Linux 可以提供低延遲和高確定性的任務調度,適用于需要高實時性的應用場景。

        3. 優化網絡配置

        調整網絡參數,如 MTU 和緩沖區大小,以減少傳輸延遲。

        4. 使用調試工具

        在開發過程中,使用調試工具(如 iperf3tcpdumpnetstat)可以幫助你更好地理解和解決網絡傳輸問題。

        5. 實現重傳機制

        在 UDP 中實現自定義的重傳機制,以處理數據包丟失。

        6. 使用 FEC

        在數據中添加冗余信息,以便在數據包丟失時恢復數據。

        總結與應用場景

        本文通過實際案例,詳細介紹了如何在實時 Linux 下實現數據流的實時傳輸,包括優化網絡傳輸延遲的技術手段及方案。實時數據流的傳輸在許多領域都有廣泛的應用,如工業自動化、金融交易、多媒體應用等。希望讀者能夠將所學知識應用到真實項目中,優化系統的實時性能。如果你有任何問題或建議,歡迎在評論區留言。

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

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

        相關文章

        C#數組(一維數組、多維數組、交錯數組、參數數組)

        在 C# 中&#xff0c;數組是一種用于存儲固定大小的相同類型元素的集合。數組可以包含值類型、引用類型或對象類型的元素&#xff0c;并且在內存中是連續存儲的。以下是關于 C# 數組的詳細介紹&#xff1a;1. 一維數組聲明與初始化// 聲明數組 int[] numbers; // 聲…

        Dify離線安裝包-集成全部插件、模板和依賴組件,方便安可內網使用

        項目介紹 Dify一鍵離線安裝包&#xff0c;集成安裝了全部插件、模板&#xff0c;并集成了dify全部插件所需的依賴組件。方便你在內網、安可環境等離線狀態下使用。 Dify是一個開源的LLM應用開發平臺。其直觀的界面結合了AI工作流、RAG管道、Agent、模型管理、可觀測性功能等&…

        面試150 翻轉二叉樹

        思路 采用先序遍歷&#xff0c;可以通過新建根節點node&#xff0c;將原來root的右子樹連到去node的左子樹中&#xff0c;root的左子樹連到去node的右子樹中。 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): …

        C++-linux系統編程 3.gcc編譯工具

        GCC編譯工具鏈完全指南 GCC&#xff08;GNU Compiler Collection&#xff09;是Linux系統下最常用的編譯器套件&#xff0c;支持C、C、Objective-C等多種編程語言。本章將深入講解GCC的編譯流程、常用選項及項目實戰技巧。 一、GCC編譯的四個核心階段 GCC編譯一個程序需要經過四…

        uView UI 組件大全

        uView UI 是一個基于 uni-app 的高質量 UI 組件庫&#xff0c;提供豐富的跨平臺組件&#xff08;支持 H5、小程序、App 等&#xff09;。以下是其核心組件的分類大全及功能說明&#xff0c;結合最新版本&#xff08;1.2.10&#xff09;整理&#xff1a; &#x1f4e6; 一、基礎…

        QWidget 和 QML 的本質和使用上的區別

        QWidget 和 QML 是 Qt 框架中兩種不同的 UI 開發技術&#xff0c;它們在底層實現、設計理念和使用場景上有顯著區別。以下是它們的本質和主要差異&#xff1a;1. 本質區別特性QWidgetQML (Qt Modeling Language)技術基礎基于 C 的面向對象控件庫基于聲明式語言&#xff08;類似…

        中轉模型服務的風險

        最近發現一些 AI 相關帖子下&#xff0c;存在低質 claude code 中轉的小廣告。 其中轉的基本原理就是 claude code 允許自己提供 API endpoint 和 key&#xff0c;可以使用任意一個 OpenAI API 兼容的供應商&#xff0c;就這么簡單。 進一點 claude token&#xff0c;再混入一點…

        前端Vue.js面試題(3)

        ???目錄 1.v-model的原理是什么樣的&#xff1f; 2.Vue的生命周期&#xff1f; 3.Vue子組件和父組件執行順序&#xff1f; 4.created和mounted的區別&#xff1f; 5.vue中&#xff0c;推薦在哪個生命周期發起請求&#xff1f; 6.keep-alive中的生命周期有哪些&#xf…

        leetcode:HJ18 識別有效的IP地址和掩碼并進行分類統計[華為機考][字符串]

        學習要點 bitset<8>ostringstreamstoistring.findstring.substr 題目鏈接 識別有效的IP地址和掩碼并進行分類統計_牛客題霸_牛客網 題目描述 解法 #include <iostream> #include <bits/stdc.h> #include <sstream> #include <string> #inclu…

        JavaEE Tomcat

        企業開發介紹 JavaEE 規范 JavaEE規范是J2EE規范的新名稱,早期被稱為 J2EE 規范,其全稱是 Java 2 Platform Enterprise Edition,是由 SUN 公司領導、各廠家共同制定并得到廣泛認可的工業標準(JCP 組織成員)。 其中,JCP 組織(官網)的全稱是 Java Community Process,…

        什么是神經網絡,常用的神經網絡,如何訓練一個神經網絡

        神經網絡&#xff1a;是深度學習的核心技術。模仿生物神經元工作方式的計算模型&#xff0c;由大量互相連接是神經元組成&#xff0c;通過數據學習復雜的模式和關系。1、神經網絡基本組成&#xff1a;神經元、層、連接神經元神經網絡的最小單元。每個神經元接受輸入&#xff0c…

        BigFoot Decursive 2.7.28 2025.07.11

        插件顯示為獨立插件&#xff0c;之前是團隊框架自帶 BigFoot Decursive lua-CSDN博客 /decursive 命令打開插件 /DCRSHOW 打開設置列表 然后優先列表里面再點【p】添加&#xff0c;你要驅散得優先職業 一鍵驅散lua插件下載&#xff1a; https://download.csdn.net/downloa…

        可穿戴智能硬件在國家安全領域的應用

        可穿戴智能硬件在國家安全領域具有廣泛應用&#xff0c;涵蓋軍事作戰、安防監控、邊境巡邏等多個方面&#xff0c;以下是具體介紹&#xff1a;軍事作戰與訓練&#xff1a;戰場態勢感知&#xff1a;士兵佩戴集成多種傳感器的智能頭盔、智能背心等&#xff0c;可實時獲取戰場環境…

        后端接口通用返回格式與異常處理實現

        前言 目前大部分系統都是前后端分離架構&#xff0c;后端提供接口并返回 JSON 數據&#xff0c;前端接收數據后進行處理展示。為了提高前后端協作效率&#xff0c;后端接口返回值采用固定格式十分必要。 后端接口返回值通用格式 通用返回值通常包含 4 個核心字段&#xff0c…

        【yolo】模型訓練參數解讀

        在YOLO&#xff08;You Only Look Once&#xff09;目標檢測模型的訓練過程中&#xff0c;數據增強是一項至關重要且極具“藝術性”的技術。它通過對訓練圖像進行一系列隨機變換&#xff0c;人為地創造出更多樣化的訓練樣本&#xff0c;從而有效提升模型的泛化能力、魯棒性&…

        IPsec:網絡層的加密盾牌與HTTPS的差異解析

        ??一、IPsec核心原理??1. 安全封裝結構?┌───────────────┬────────────────┬──────────────────────┐ │ IP頭部 │ IPSec頭部 │ 加密/認證的載荷 │ │ (路由尋址) │ (AH/ESP) │…

        【Python辦公】Python如何批量提取PDF中的表格

        目錄 專欄導讀概述主要工具庫介紹1. tabula-py2. camelot-py3. pdfplumber4. PyMuPDF (fitz)環境準備安裝依賴Java環境配置(tabula-py需要)方法一:使用tabula-py提取表格基礎用法高級配置方法二:使用camelot-py提取表格方法三:使用pdfplumber提取表格批量處理多個PDF文件數…

        MySQL自定義order by排序規則

        數據表create table tb_user (id bigint auto_incrementprimary key,name varchar(16) not null,age int not null,address varchar(128) null );INSERT INTO test.tb_user (id, name, age, address) VALUES (1, 張三, 18, China); INSERT INTO test.tb_…

        112套開題答辯行業PPT模版

        畢業答辯開題報告&#xff0c;畢業答辯&#xff0c;論文設計PPT&#xff0c;清新論文答辯PPT模版&#xff0c;畢業論文答辯開題報告PPT&#xff0c;答辯演講通用PPT模版&#xff0c;文藝時尚畢業答辯PPT模版&#xff0c;簡約畢業論文答辯PPT模版112套開題答辯行業PPT模版&#…

        驅動開發系列61- Vulkan 驅動實現-SPIRV到HW指令的實現過程(2)

        本節繼續介紹下SPIR-V到LLVM IR的轉換過程,重點分析其核心機制和關鍵轉換步驟。我們將從 LLVM 入手,結合實SPIR-V結構逐步轉換為符合 LLVM IR 語義的表示方式。 一:詳細過程 1. 創建llvm::module llvm::LLVMContext llvmContext; std::unique_ptr<llvm::Mod…