首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

Winsock UDP 實現語音全雙工通訊:架構與關鍵 API 解析

     編輯:VC 教學

為何選 UDP?

  • TCP:可靠、有序、流控制,但延遲不可控、標頭開銷大,**不適合即時語音**。
  • UDP:無連接、不可靠、無序,但**延遲低、開銷小、支援廣播/多播**,適合即時語音/視訊。應用層自行處理丟包隱藏 (FEC、PLC)、抖動緩衝、序號重排。

全雙工架構:雙 Socket 設計

為了同時收發不阻塞,建立兩個 UDP Socket:

  • 發送 Socket:綁定本地埠 P1,sendto 發送編碼後語音封包至對方 IP:P2。
  • 接收 Socket:綁定本地埠 P2,recvfrom 循環接收封包,解碼播放。

關鍵:兩 Socket 必須綁定不同埠號,且需設定 SO_REUSEADDR 允許地址重用 (若同一程式多實例或快速重啟)。

核心 API 流程

// 1. 初始化
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData);

// 2. 建立發送 Socket
SOCKET sSend = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
BOOL bReuse = TRUE;
setsockopt(sSend, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(bReuse));
sockaddr_in localSend = {0};
localSend.sin_family = AF_INET;
localSend.sin_port = htons(LOCAL_SEND_PORT);
localSend.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sSend, (sockaddr*)&localSend, sizeof(localSend));

// 3. 建立接收 Socket
SOCKET sRecv = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
setsockopt(sRecv, SOL_SOCKET, SO_REUSEADDR, (char*)&bReuse, sizeof(bReuse));
sockaddr_in localRecv = {0};
localRecv.sin_family = AF_INET;
localRecv.sin_port = htons(LOCAL_RECV_PORT);
localRecv.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sRecv, (sockaddr*)&localRecv, sizeof(localRecv));

// 4. 設定接收緩衝區 (建議 8KB~64KB)
int bufSize = 32768;
setsockopt(sRecv, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, sizeof(bufSize));

// 5. 目標地址
sockaddr_in remote = {0};
remote.sin_family = AF_INET;
remote.sin_port = htons(REMOTE_PORT);
remote.sin_addr.s_addr = inet_addr("192.168.1.100");

// 6. 發送執行緒
while (running) {
    // 從音訊捕獲裝置取得 PCM 資料 → 編碼 (G.711/G.729/Speex/Opus) → 封裝 RTP
    sendto(sSend, encodedData, encodedLen, 0, (sockaddr*)&remote, sizeof(remote));
    Sleep(20);  // 20ms 幀間隔
}

// 7. 接收執行緒
sockaddr_in from;
int fromLen = sizeof(from);
while (running) {
    int n = recvfrom(sRecv, buffer, sizeof(buffer), 0, (sockaddr*)&from, &fromLen);
    if (n > 0) {
        // 驗證來源、解析 RTP、解碼 → 送入播放緩衝隊列
    }
}

// 8. 清理
closesocket(sSend);
closesocket(sRecv);
WSACleanup();

語音處理管線 (應用層責任)

  1. 捕獲:waveInOpen / WASAPI / PortAudio 取得 8k/16k Hz 單聲道 PCM。
  2. 編碼:G.711 (無壓縮、低延遲)、Speex/Opus (低碼率、高品質)。
  3. 封裝:加上 RTP 標頭 (序號、時間戳、SSRC、負載類型)。
  4. 傳輸:UDP 發送。
  5. 接收端:抖動緩衝 (Jitter Buffer) → 序號重排 → 丟包隱藏 (PLC) → 解碼 → 播放隊列。
  6. 播放:waveOutOpen / WASAPI 低延遲播放。

NAT 穿透提示

  • 雙方均在 NAT 後:需 STUN/TURN/ICE 或 UPnP 映射埠口。
  • 簡易方案:雙方約定固定埠,路由器設定「埠觸發」或「DMZ」。

總結

UDP + 雙 Socket + 應用層可靠性機制 (RTP/RTCP、編碼、抖動緩衝) 是語音全雙工通訊的標準架構。Winsock 提供底層傳輸,核心難點在音訊管線與網路抖動對抗,而非 Socket API 本身。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved