【RTSP從零實踐】4、使用RTP協議封裝并傳輸AAC

😁博客主頁😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客內容🤑:🍭嵌入式開發、Linux、C語言、C++、數據結構、音視頻🍭
🤣本文內容🤣:🍭介紹怎么使用RTP協議封裝并傳輸AAC🍭
😎金句分享😎:🍭你不能選擇最好的,但最好的會來選擇你——泰戈爾🍭
?發布時間?: 2025-07-01 18:43:18

本文未經允許,不得轉發!!!

目錄

  • 🎄一、概述
  • 🎄二、實現步驟、實現細節
    • ?2.1、實現AAC文件讀取器
    • ?2.2、實現 AAC 的 RTP 數據包封裝
      • 🎈2.2.1、RTP頭(RTP Header)
      • 🎈2.2.2、AAC 的 RTP負載(RTP Payload)
    • ?2.3、SDP協議介紹
  • 🎄三、RTP協議封裝并傳輸AAC的實現源碼
  • 🎄四、總結


在這里插入圖片描述

在這里插入圖片描述

🎄一、概述

前面文章介紹了怎么實現RTSP協議、RTP協議傳輸H264等:
【RTSP從零實踐】1、根據RTSP協議實現一個RTSP服務
【RTSP從零實踐】2、使用RTP協議封裝并傳輸H264
【RTSP從零實踐】3、實現最簡單的傳輸H264的RTSP服務器

這篇文章介紹使用RTP協議封裝并傳輸AAC。

首先,需要對 AAC文件結構 有了解,我們要從一個AAC文件讀取音頻幀,再進行RTP封包發送。本文會介紹需要用到的AAC相關知識,需要了解更詳細的可以參考以前文章:AAC格式音頻文件解析。

然后,需要對 RTP包結構 有個了解,本文介紹的RTP包由2部分組成:RTP頭、RTP負載。需要了解RTP頭數據結構、RTP負載數據結構,下個小節會介紹怎樣用代碼實現。對RTP數據包格式需要了解更多的可以參考這篇文章:RTP協議詳解 。

最后,需要對 SDP協議 有了解,后面代碼運行后,需要使用vlc打開.sdp文件來驗證功能,所以需要知道一個sdp文件的內容是什么意思,這個將會在2.3小節介紹,如果需要了解更多sdp協議知識,可以看這篇文章:SDP(會話描述協議)詳解 及 抓包例子分析。


在這里插入圖片描述

🎄二、實現步驟、實現細節

這個小寫介紹一些基礎知識點,下個小節會提供源碼,可以結合著源碼看幫助理解消化。

?2.1、實現AAC文件讀取器

在這里插入圖片描述

.aac文件保存了AAC編碼的音頻幀,在ADTS格式的aac文件中,每個音頻幀都包含了一個ADTS headerAAC ES。而ADTS header占了7個字節,且最開始表示同步碼(syncword)的12bit的所有的bit位都是1,總是0xFFF,代表一個ADTS幀的開始,作為分界符,用于同步每幀起始位置。在可變頭部有表示aac幀長的aac_frame_length,占13bit。我們可以用下面代碼來查找同步碼并獲取幀長。
在這里插入圖片描述

清楚上述知識后,我們就可以從aac文件結構不斷讀取音頻幀數據了。


?2.2、實現 AAC 的 RTP 數據包封裝

本文介紹的RTP數據包主要由2部分組成,即 RTP頭、RTP負載。

🎈2.2.1、RTP頭(RTP Header)

在這里插入圖片描述
上圖是RTP頭的結構圖,包含了12個字節的內容,可以用代碼定義成如下結構體:

struct RtpHeader
{/* byte 0 */uint8_t csrcLen:4;uint8_t extension:1;uint8_t padding:1;uint8_t version:2;/* byte 1 */uint8_t payloadType:7;uint8_t marker:1;/* bytes 2,3 */uint16_t seq;/* bytes 4-7 */uint32_t timestamp;/* bytes 8-11 */uint32_t ssrc;
};

RTP頭這里涉及到一個 時間戳怎么計算 的問題,需要注意的是,這個時間戳是一個 時鐘頻率 為單位的,而不是具體的時間(秒、毫秒等)。
一般情況下,AAC每個1024個采樣為一幀。假設AAC的時鐘頻率為48000Hz,所以一秒就有 48000 / 1024 = 47幀,那么每一幀的 時間間隔 就是1/47秒,每一幀的 時鐘增量 就是(48000 / 47 = 1021)。
那時間戳怎么算呢?舉個例子,以上面計算的數據,第一幀的RTP時間戳為0的話,那么第二幀的RTP時間戳就是 1021,第三幀的RTP時間戳就是 (1021+1021),依次類推,后一幀的RTP時間戳在前一幀的RTP時間戳的值加上一個時鐘增量

在這里插入圖片描述
注意:RTP的時間戳計算很重要,我一開始沒懂時間戳的概念,導致播放的聲音斷斷續續的。


🎈2.2.2、AAC 的 RTP負載(RTP Payload)

RTP負載常用的有兩種方式,第一種是 單個NAL單元封包(Single NAL Unit Packet);第二種是 分片單元(Fragmentation Unit) 。因為一幀ADTS幀一般小于 MTU(網絡最大傳輸單元1500字節),所以對于AAC的RTP封包只需要采用 單個NAL單元封包(Single NAL Unit Packet) 即可。

但并不是直接將 ADTS 幀去掉ADTS頭之后的數據 作為RTP負載,AAC的RTP負載最開始有4個字節,其中2個字節表示AU頭長度(AU-headers-length),13bit的AU size;3bit的AU-Index(-delta) field。如下圖:

所以,AAC的RTP負載的一個字節為0x00,第二個字節為0x10,第三個字節和第四個字節保存AAC Data的大小,最多只能保存13bit(也就是說,第三個字節保存數據大小的高八位,第四個字節的高5位保存數據大小的低5位)。

參考下列代碼:

rtpPacket->payload[0] = 0x00;
rtpPacket->payload[1] = 0x10;
rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5; // 高8位
rtpPacket->payload[3] = (frameSize & 0x1F) << 3;   // 低5位

這4個字節之后就是 ADTS 幀去掉ADTS頭之后的數據 了。


?2.3、SDP協議介紹

本文是使用VLC打開.sdp文件來驗證功能的,所以也需要知道SDP協議的一些知識,下面是本文使用的sdp文件:

m=audio 9832 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/48000/2
a=fmtp:97 SizeLength=13;
c=IN IP4 192.168.2.180
  • m=audio 9832 RTP/AVP 97 :表示這是一個媒體描述,媒體類型是audio;接收媒體流的端口號是9832;傳輸協議是RTP/AVP(通過UDP傳輸);RTP負載類型為97。

  • a=rtpmap:97 mpeg4-generic/48000/2:RTP負載類型的具體編碼是 mpeg4-generic(AAC屬于mpeg4的),時鐘頻率為 48000Hz,2個聲道。

  • a=fmtp:97 SizeLength=13;:幀長用13個bit表示。

  • c=IN IP4 192.168.2.180:表示連接信息,網絡類型為IN,表示Internet。地址類型為IP4,表示IPv4地址。接收端地址為192.168.2.180。


在這里插入圖片描述

🎄三、RTP協議封裝并傳輸AAC的實現源碼

總共有5個源代碼文件,1個sdp文件,具體如下:

1、aacReader.h

/*** @file aacReader.h* @author : https://blog.csdn.net/wkd_007* @brief * @version 0.1* @date 2025-06-30* * @copyright Copyright (c) 2025* */
#ifndef	__AAC_READER_H__
#define __AAC_READER_H__#include <stdio.h>#define ADTS_HEADER_LEN	(7)typedef struct
{int frame_len;                //! unsigned char *pFrameBuf;     //! 
} AACFrame_t;typedef struct AACReaderInfo_s
{FILE *pFileFd;
}AACReaderInfo_t;int AAC_FileOpen(char *fileName, AACReaderInfo_t *pAACInfo);
int AAC_FileClose(AACReaderInfo_t *pAACInfo);
int AAC_GetADTSFrame(AACFrame_t *pAACFrame, const AACReaderInfo_t *pAACInfo);
int AAC_IsEndOfFile(const AACReaderInfo_t *pAACInfo);
void AAC_SeekFile(const AACReaderInfo_t *pAACInfo);#endif 	// __AAC_READER_H__

2、aacReader.c

/*** @file aacReader.c* @author : https://blog.csdn.net/wkd_007* @brief * @version 0.1* @date 2025-06-30* * @copyright Copyright (c) 2025* */
#include <stdlib.h>
#include <string.h>
#include "aacReader.h"#define MAX_FRAME_LEN (1024*1024)	// 一幀數據最大字節數
#define MAX_SYNCCODE_LEN    (3)     // 同步碼字節個數 2025-05-21 17:45:06static int findSyncCode_0xFFF(unsigned char *Buf, int *size)
{if((Buf[0] == 0xff) && ((Buf[1] & 0xf0) == 0xf0) )//0xFF F,前12bit都為1 2025-05-21 17:46:57{*size |= ((Buf[3] & 0x03) <<11);     //high 2 bit*size |= Buf[4]<<3;                //middle 8 bit*size |= ((Buf[5] & 0xe0)>>5);        //low 3bitreturn 1;}return 0;
}int AAC_FileOpen(char *fileName, AACReaderInfo_t *pAACInfo)
{pAACInfo->pFileFd = fopen(fileName, "rb+");if (pAACInfo->pFileFd==NULL){printf("[%s %d]Open file error\n",__FILE__,__LINE__);return -1;}return 0;
}int AAC_FileClose(AACReaderInfo_t *pAACInfo)
{if (pAACInfo->pFileFd != NULL) {fclose(pAACInfo->pFileFd);pAACInfo->pFileFd = NULL;}return 0;
}int AAC_IsEndOfFile(const AACReaderInfo_t *pAACInfo)
{return feof(pAACInfo->pFileFd);
}void AAC_SeekFile(const AACReaderInfo_t *pAACInfo)
{fseek(pAACInfo->pFileFd,0,SEEK_SET);
}/*** @brief * * @param pAACFrame :輸出參數,使用后 pAACInfo->pFrameBuf 需要free* @param pAACInfo * @return int */
int AAC_GetADTSFrame(AACFrame_t *pAACFrame, const AACReaderInfo_t *pAACInfo)
{int rewind = 0;if (pAACInfo->pFileFd==NULL){printf("[%s %d]pFileFd error\n",__FILE__,__LINE__);return -1;}// 1.先讀取ADTS幀頭(7個字節)unsigned char* pFrame = (unsigned char*)malloc(MAX_FRAME_LEN);int readLen = fread(pFrame, 1, ADTS_HEADER_LEN, pAACInfo->pFileFd);if(readLen <= 0){printf("[%s %d]fread error readLen=%d\n",__FILE__,__LINE__,readLen);free(pFrame);return -1;}// 2.查找當前幀同步碼,獲取幀長度int i=0;int size = 0;for(; i<readLen-MAX_SYNCCODE_LEN; i++){if(!findSyncCode_0xFFF(&pFrame[i], &size)){continue;}else{break;}}if(i!=0)	// 不是幀開頭,偏移到幀開頭重新讀{printf("[%s %d]synccode error, i=%d\n",__FILE__,__LINE__,i);free(pFrame);rewind = (-(readLen-i));fseek (pAACInfo->pFileFd, rewind, SEEK_CUR);return -1;}// 3.讀取ADTS幀數據 2025-05-22 21:44:39readLen = fread(pFrame+ADTS_HEADER_LEN, 1, size-ADTS_HEADER_LEN, pAACInfo->pFileFd);if(readLen <= 0){printf("[%s %d]fread error\n",__FILE__,__LINE__);free(pFrame);return -1;}// 4.填數據pAACFrame->frame_len = size;pAACFrame->pFrameBuf = pFrame;return pAACFrame->frame_len;
}

3、rtp.h

#ifndef _RTP_H_
#define _RTP_H_
#include <stdint.h>#define RTP_VESION              2#define RTP_PAYLOAD_TYPE_H264   96
#define RTP_PAYLOAD_TYPE_AAC    97#define RTP_HEADER_SIZE         12
#define RTP_MAX_PKT_SIZE        1400/***    0                   1                   2                   3*    7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*   |V=2|P|X|  CC   |M|     PT      |       sequence number         |*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*   |                           timestamp                           |*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*   |           synchronization source (SSRC) identifier            |*   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+*   |            contributing source (CSRC) identifiers             |*   :                             ....                              :*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+**/
struct RtpHeader
{/* byte 0 */uint8_t csrcLen:4;uint8_t extension:1;uint8_t padding:1;uint8_t version:2;/* byte 1 */uint8_t payloadType:7;uint8_t marker:1;/* bytes 2,3 */uint16_t seq;/* bytes 4-7 */uint32_t timestamp;/* bytes 8-11 */uint32_t ssrc;
};struct RtpPacket
{struct RtpHeader rtpHeader;uint8_t payload[0];
};void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,uint16_t seq, uint32_t timestamp, uint32_t ssrc);
int rtpSendPacket(int socket, char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize);#endif //_RTP_H_

4、rtp.c

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "rtp.h"void rtpHeaderInit(struct RtpPacket* rtpPacket, uint8_t csrcLen, uint8_t extension,uint8_t padding, uint8_t version, uint8_t payloadType, uint8_t marker,uint16_t seq, uint32_t timestamp, uint32_t ssrc)
{rtpPacket->rtpHeader.csrcLen = csrcLen;rtpPacket->rtpHeader.extension = extension;rtpPacket->rtpHeader.padding = padding;rtpPacket->rtpHeader.version = version;rtpPacket->rtpHeader.payloadType =  payloadType;rtpPacket->rtpHeader.marker = marker;rtpPacket->rtpHeader.seq = seq;rtpPacket->rtpHeader.timestamp = timestamp;rtpPacket->rtpHeader.ssrc = ssrc;
}int rtpSendPacket(int socket, char* ip, int16_t port, struct RtpPacket* rtpPacket, uint32_t dataSize)
{struct sockaddr_in addr;int ret;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip);rtpPacket->rtpHeader.seq = htons(rtpPacket->rtpHeader.seq);rtpPacket->rtpHeader.timestamp = htonl(rtpPacket->rtpHeader.timestamp);rtpPacket->rtpHeader.ssrc = htonl(rtpPacket->rtpHeader.ssrc);ret = sendto(socket, (void*)rtpPacket, dataSize+RTP_HEADER_SIZE, 0,(struct sockaddr*)&addr, sizeof(addr));rtpPacket->rtpHeader.seq = ntohs(rtpPacket->rtpHeader.seq);rtpPacket->rtpHeader.timestamp = ntohl(rtpPacket->rtpHeader.timestamp);rtpPacket->rtpHeader.ssrc = ntohl(rtpPacket->rtpHeader.ssrc);return ret;
}

5、rtp_aac_main.c

/*** @file rtp_aac_main.c* @author : https://blog.csdn.net/wkd_007* @brief * @version 0.1* @date 2025-06-30* * @copyright Copyright (c) 2025* */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>#include "rtp.h"
#include "aacReader.h"#define AAC_FILE_NAME "test.aac"
#define CLIENT_IP "192.168.2.180" // 運行vlc打開sdp文件的電腦IP
#define CLIENT_PORT 9832static int createUdpSocket()
{int fd = socket(AF_INET, SOCK_DGRAM, 0);if (fd < 0)return -1;int on = 1;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));return fd;
}static int rtpSendAACFrame(int socket, char *ip, int16_t port,struct RtpPacket *rtpPacket, uint8_t *frame, uint32_t frameSize)
{int ret;rtpPacket->payload[0] = 0x00;rtpPacket->payload[1] = 0x10;rtpPacket->payload[2] = (frameSize & 0x1FE0) >> 5; // 高8位rtpPacket->payload[3] = (frameSize & 0x1F) << 3;   // 低5位memcpy(rtpPacket->payload + 4, frame, frameSize);ret = rtpSendPacket(socket, ip, port, rtpPacket, frameSize + 4);if (ret < 0){printf("failed to send rtp packet\n");return -1;}rtpPacket->rtpHeader.seq++;return 0;
}int main(int argc, char *argv[])
{int socket = createUdpSocket();if (socket < 0){printf("failed to create socket\n");return -1;}struct RtpPacket *rtpPacket = (struct RtpPacket *)malloc(sizeof(struct RtpPacket) + 1500);rtpHeaderInit(rtpPacket, 0, 0, 0, RTP_VESION, RTP_PAYLOAD_TYPE_AAC, 1, 0, 0, 0x32411);// aacAACReaderInfo_t aacInfo;if (AAC_FileOpen(AAC_FILE_NAME, &aacInfo) < 0){printf("failed to open %s\n", AAC_FILE_NAME);return -1;}while (1){if (!AAC_IsEndOfFile(&aacInfo)){AACFrame_t aacFrame;memset(&aacFrame, 0, sizeof(aacFrame));AAC_GetADTSFrame(&aacFrame, &aacInfo);if (aacFrame.pFrameBuf != NULL){// printf("rtpSendAACFrame\n");rtpSendAACFrame(socket, CLIENT_IP, CLIENT_PORT, rtpPacket,aacFrame.pFrameBuf + ADTS_HEADER_LEN, aacFrame.frame_len - ADTS_HEADER_LEN);free(aacFrame.pFrameBuf);/** 如果采樣頻率是48000* 一般AAC每個1024個采樣為一幀* 所以一秒就有 48000 / 1024 = 47幀* 時間增量就是 48000 / 47 = 1021* 一幀的時間為 1000ms / 47 = 21ms*/rtpPacket->rtpHeader.timestamp += 1021;usleep(21 * 1000);}else{printf("warning SeekFile\n");AAC_SeekFile(&aacInfo);}}}free(rtpPacket);return 0;
}

6、rtp_aac.sdp

m=audio 9832 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/48000/2
a=fmtp:97 SizeLength=13;
c=IN IP4 192.168.2.180

將上面代碼保存在同一個目錄后,并且在同目錄里放一個.aac文件,然后運行 gcc *.c 編譯,再執行./a.out運行程序,下面是我運行的過程:
在這里插入圖片描述


在這里插入圖片描述

🎄四、總結

本文介紹了實現 使用RTP協議封裝并傳輸 AAC 的一些步驟和細節,介紹了RTP封包格式,sdp相關知識等,也提供了實現源碼和運行結果,可以幫助讀者快速了解RTP協議怎么傳輸AAC數據的。

在這里插入圖片描述
如果文章有幫助的話,點贊👍、收藏?,支持一波,謝謝 😁😁😁

參考:
https://blog.csdn.net/huabiaochen/article/details/104576088

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

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

相關文章

Bootstrap 安裝使用教程

一、Bootstrap 簡介 Bootstrap 是一個開源的前端框架&#xff0c;由 Twitter 開發&#xff0c;旨在快速開發響應式、移動優先的 Web 頁面。它包含 HTML、CSS 和 JavaScript 組件&#xff0c;如按鈕、導航欄、表單等。 二、Bootstrap 安裝方式 2.1 使用 CDN&#xff08;推薦入…

Java學習第二部分——基礎語法

目錄 一.數據類型 &#xff08;一&#xff09;數值類型&#xff08;用于存儲數字&#xff0c;包括整數和浮點數&#xff09; 1. **整數類型** 2. **浮點類型** &#xff08;二&#xff09;非數值類型&#xff08;非數值類型用于存儲非數字數據&#xff09; 1. **char** 2…

Redis分布式鎖核心原理源碼

文章目錄 概述一、Redis實現分布式鎖1.1、第一版1.2、第二版1.3、第三版1.3、第四版 二、Redisson實現分布式鎖核心源碼分析2.1、加鎖核心源碼2.2、鎖續期核心源碼2.3、重試機制核心源碼2.4、解鎖核心源碼 總結 概述 傳統的單機鎖&#xff08;Synchronized&#xff0c;Reentran…

關于vue2使用elform的rules校驗

在使用vue2開發項目的時候使用element組件的el-form大多數情況都需要用到必填項校驗 舉個栗子&#xff1a; <el-form :model"ruleForm" :rules"rules" ref"ruleForm" label-width"100px" class"demo-ruleForm"><e…

langchain從入門到精通(二十六)——RAG優化策略(四)問題分解策略提升負責問題檢索準確率

1. LangChain 少量示例提示模板 在與 LLM 的對話中&#xff0c;提供少量的示例被稱為 少量示例&#xff0c;這是一種簡單但強大的指導生成的方式&#xff0c;在某些情況下可以顯著提高模型性能&#xff08;與之對應的是零樣本&#xff09;&#xff0c;少量示例可以降低 Prompt…

Nuxt.js基礎(Tailwind基礎)

??1. 按鈕組件實現?? ??傳統 CSS <!-- HTML --> <button class"btn-primary">提交</button><!-- CSS --> <style>.btn-primary {background-color: #3490dc;padding: 0.5rem 1rem;border-radius: 0.25rem;color: white;transi…

[C語言]存儲結構詳解

C語言存儲結構總結 在C語言中&#xff0c;數據根據其類型和聲明方式被存儲在不同的內存區域。以下是各類數據存儲位置的詳細總結&#xff1a; 內存五大分區 存儲區存儲內容生命周期特點代碼區(.text)程序代碼(機器指令)整個程序運行期只讀常量區(.rodata)字符串常量、const全…

【實戰】 容器中Spring boot項目 Graphics2D 畫圖中文亂碼解決方案

場景 架構&#xff1a;spring boot 容器技術&#xff1a;docker 服務器&#xff1a;阿里云 開發環境&#xff1a;windows10 IDEA 一、問題 服務器中出現Graphics2D 畫圖中文亂碼 本地環境運行正常 二、原因 spring boot 容器中沒有安裝中文字體 三、解決方案 安裝字體即可 …

深入淺出:Vue2 數據劫持原理剖析

目錄 一、什么是數據劫持&#xff1f; 二、核心 API&#xff1a;Object.defineProperty 三、Vue2 中的數據劫持實現 1. 對象屬性的劫持 2. 嵌套對象的處理 3. 數組的特殊處理 四、結合依賴收集的完整流程 五、數據劫持的局限性 六、Vue3 的改進方案 總結 一、什么是數…

數據湖 vs 數據倉庫:數據界的“自來水廠”與“瓶裝水廠”?

數據湖 vs 數據倉庫&#xff1a;數據界的“自來水廠”與“瓶裝水廠”&#xff1f; 說起“數據湖”和“數據倉庫”&#xff0c;很多剛入行的朋友都會覺得&#xff1a; “聽起來好高大上啊&#xff01;但到底有啥區別啊&#xff1f;是湖更大還是倉庫更高端&#xff1f;” 我得說…

Node.js-path模塊

Path 模塊 path 模塊提供了 操作路徑 的功能&#xff0c;我們將介紹如下幾個較為常用的幾個 API ??path.resolve([…paths]) 將路徑片段??解析為絕對路徑??&#xff08;從右向左拼接&#xff0c;遇到絕對路徑停止&#xff09; // 若參數為空&#xff0c;返回當前工作目…

Java面試題029:一文深入了解MySQL(1)

歡迎大家關注我的專欄,該專欄會持續更新,從原理角度覆蓋Java知識體系的方方面面。 一文吃透JAVA知識體系(面試題)https://blog.csdn.net/wuxinyan123/category_7521898.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=7521898&

vue3.0所采用得Composition Api與Vue2.XOtions Api有什么不同?

Vue 3.0 引入的 Composition API 相較于 Vue 2.x 的 Options API 有顯著的不同。下面從幾個方面對比這兩者的差異&#xff1a; ? 1. 代碼組織方式不同 Vue 2.x — Options API 使用 data、methods、computed、watch 等分散的選項組織邏輯。 每個功能點分散在不同的選項中&am…

【IP 潮玩行業深度研究與學習】

潮玩行業發展趨勢分析&#xff1a;全球市場格局與中國政策支持體系 潮玩產業正經歷從"小眾收藏"到"大眾情緒消費"的深刻轉型&#xff0c;2025年中國潮玩市場規模已達727億元&#xff0c;預計2026年將突破1100億元&#xff0c;年復合增長率高達26%。這一千…

進程通信-消息隊列

消息隊列允許一個進程將一個消息發送到一個隊列中&#xff0c;另一個進程從該隊列中接收這個消息。 使用流程&#xff1a; 寫端&#xff1a; 使用結構體 mq_attr 設置消息隊列屬性&#xff0c;有四個選項&#xff1a; long mq_flags; // 隊列屬性: 0 表示阻塞 long …

串行通信接口USART,printf重定向數據發送,輪詢和中斷實現串口數據接收

目錄 UART通信協議的介紹 實現串口數據發送 CubeMX配置 printf重定向代碼編寫 實現串口數據接收 輪詢方式實現串口數據接收 接收單個字符 接收不定長字符串&#xff08;接收的字符串以\n結尾&#xff09; 中斷方式實現串口數據接收 CubeMX配置 UART中斷方式接收數據…

Kafka 生產者和消費者高級用法

Kafka 生產者和消費者高級用法 1 生產者的事務支持 Kafka 從版本0.11開始引入了事務支持&#xff0c;使得生產者可以實現原子操作&#xff0c;確保消息的可靠性。 // 示例代碼&#xff1a;使用 Kafka 事務 producer.initTransactions(); try {producer.beginTransaction();pr…

k8s中crictl命令常報錯解決方法

解決使用crictl命令時報默認端點棄用的報錯 報錯核心原因 默認端點棄用&#xff1a; crictl 會默認嘗試多個容器運行時端點&#xff08;如 dockershim.sock、containerd.sock 等&#xff09;&#xff0c;但這種 “自動探測” 方式已被 Kubernetes 棄用&#xff08;官方要求手動…

回轉體水下航行器簡單運動控制的奧秘:PID 控制和水動力方程的運用

在水下航行器的控制領域中&#xff0c;回轉體水下航行器的運動控制是一個關鍵課題。 今天&#xff0c;就來深入探討一下其簡單運動控制中&#xff0c;PID 控制以及水動力方程的相關運用。 PID 控制的基本原理PID 控制&#xff08;比例 - 積分 - 微分控制&#xff09;是一種廣…

從入門到精通:npm、npx、nvm 包管理工具詳解及常用命令

目錄 1. 引言2. npm (Node Package Manager)2.1 定義與用途2.2 常見命令2.3 使用示例 3. npx (Node Package Execute)3.1 定義與用途3.2 常見命令3.3 使用示例3.4 npm 與 npx 的區別 4. nvm (Node Version Manager)4.1 定義與用途4.2 安裝 nvm4.3 常見命令4.4 使用示例 5. 工具…