數據包發送方式只有一個接受方,稱為單播。如果同時發給局域網中的所有主機,稱為廣播。只有用戶數據報(使用UDP協議)套接字才能廣播:
廣播地址以192.168.1.0 (255.255.255.0) 網段為例,最大的主機地址192.168.1.255代表該網段的廣播地址,發到該地址的數據包被所有的主機接收。255.255.255.255在所有網段中都代表廣播地址。廣播發送創建用戶數據報套接字缺省創建的套接字不允許廣播數據包,需要設置屬性。(setsockopt可以設置套接字屬性)接收方地址指定為廣播地址指定端口信息發送數據包setsockoptint setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);頭文件:<sys/socket.h>level : 選項級別(例如SOL_SOCKET)optname : 選項名(例如SO_BROADCAST)optval : 存放選項值的緩沖區的地址optlen : 緩沖區長度返回值:成功返回0 失敗返回-1并設置errno廣播發送示例sockfd = socket(,,);……int on = 1;setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));……sendto(;;;;;);廣播接收創建用戶數據報套接字綁定IP地址(廣播IP或0.0.0.0)和端口(綁定的端口必須和發送方指定的端口相同)等待接收數據
UDP 廣播服務器與客戶端(C 語言實現)
- 廣播(Broadcast):將數據發送到 整個局域網,所有主機都能接收。
- UDP 支持廣播,但 TCP 不支持 廣播。
- 廣播地址示例:
192.168.1.255
(局域網廣播,子網掩碼255.255.255.0
)。255.255.255.255
(全網廣播)。
關鍵 API
函數 | 功能 |
---|---|
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); | 允許 UDP 廣播 |
sendto(sockfd, message, len, 0, (struct sockaddr*)&addr, addr_len); | 發送廣播消息 |
recvfrom(sockfd, buffer, len, 0, (struct sockaddr*)&addr, &addr_len); | 接收廣播消息 |
UDP 廣播服務器完整實現代碼
服務端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BROADCAST_PORT 8080 // 廣播端口
#define BROADCAST_ADDR "255.255.255.255" // 廣播地址
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in broadcast_addr;char message[BUFFER_SIZE] = "這是來自服務器的廣播消息!";int broadcast_permit = 1;// 1?? 創建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("? 創建套接字失敗");exit(EXIT_FAILURE);}// 2?? 允許發送廣播setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_permit, sizeof(broadcast_permit));// 3?? 設置廣播地址memset(&broadcast_addr, 0, sizeof(broadcast_addr));broadcast_addr.sin_family = AF_INET;broadcast_addr.sin_port = htons(BROADCAST_PORT);broadcast_addr.sin_addr.s_addr = inet_addr(BROADCAST_ADDR);printf("服務器開始廣播消息...\n");while (1) {// 4?? 發送廣播消息sendto(sockfd, message, strlen(message), 0,(struct sockaddr*)&broadcast_addr, sizeof(broadcast_addr));printf("發送廣播消息: %s\n", message);sleep(3); // 每 3 秒廣播一次}close(sockfd);return 0;
}
客戶端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BROADCAST_PORT 8080 // 監聽廣播端口
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in listen_addr;char buffer[BUFFER_SIZE];socklen_t addr_len = sizeof(listen_addr);// 1?? 創建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("? 創建套接字失敗");exit(EXIT_FAILURE);}// 2?? 允許端口重用(多個客戶端可綁定相同端口)int reuse = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));// 3?? 綁定監聽端口memset(&listen_addr, 0, sizeof(listen_addr));listen_addr.sin_family = AF_INET;listen_addr.sin_port = htons(BROADCAST_PORT);listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(sockfd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) < 0) {perror("? 綁定失敗");close(sockfd);exit(EXIT_FAILURE);}printf("客戶端正在監聽廣播消息...\n");while (1) {// 4?? 接收廣播消息memset(buffer, 0, BUFFER_SIZE);recvfrom(sockfd, buffer, BUFFER_SIZE, 0,(struct sockaddr*)&listen_addr, &addr_len);printf("📩 收到廣播消息: %s\n", buffer);}close(sockfd);return 0;
}
運行步驟:
- 編譯
gcc udp_broadcast_server.c -o udp_broadcast_server
gcc udp_broadcast_client.c -o udp_broadcast_client
- 運行服務器(廣播消息)
./udp_broadcast_server
輸出示例:
? 服務器開始廣播消息...
📡 發送廣播消息: 這是來自服務器的廣播消息!
📡 發送廣播消息: 這是來自服務器的廣播消息!
- 運行客戶端(監聽廣播)
./udp_broadcast_client
輸出示例:
客戶端正在監聽廣播消息...
📩 收到廣播消息: 這是來自服務器的廣播消息!
📩 收到廣播消息: 這是來自服務器的廣播消息!
廣播流程思路解析(逐幀解釋)流程上面的注釋已經寫了,但是這里是配合完整的代碼實現和業務流程再過一遍
🔹 服務器
- 創建 UDP 套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- 允許廣播
int broadcast_permit = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_permit, sizeof(broadcast_permit));
- 設置廣播地址
broadcast_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
- 發送廣播消息
sendto(sockfd, message, strlen(message), 0, (struct sockaddr*)&broadcast_addr, sizeof(broadcast_addr));
🔹 客戶端
- 創建 UDP 套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- 允許端口重用
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
- 綁定監聽端口
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
- 接收廣播消息
recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&listen_addr, &addr_len);
廣播代碼優化:
- 使用 SO_REUSEADDR
- 允許多個客戶端監聽相同端口,防止端口占用問題:
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
- 限制廣播頻率:
sleep(3); // 控制廣播間隔,避免網絡擁塞。
- 支持多播:監聽 指定的多播組,而不是整個局域網:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1");
mreq.imr_interface.s_addr = INADDR_ANY;
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
UDP 廣播通信,適用于 局域網設備發現、日志廣播、狀態同步等應用。相比 TCP,UDP 廣播更加輕量級,可用于 IoT、在線游戲等場景。
在編譯過程中可能遇到的問題
🚨 1. Permission denied(權限錯誤)
如果 sendto()
失敗,檢查 SO_BROADCAST 是否設置:
int broadcast_permit = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_permit, sizeof(broadcast_permit));
若仍然失敗,可以嘗試 sudo
運行:
sudo ./udp_broadcast_server
🚨 2. Address already in use(端口占用)
如果 bind()
失敗,嘗試釋放端口:
sudo fuser -k 8080/udp
然后重新運行程序。
🚨 3. recvfrom()
接收不到消息
- 確保服務器和客戶端在 同一局域網。
- 使用
ifconfig
/ip a
檢查本機 IP 地址并使用正確的廣播地址:
ifconfig | grep "inet "
- 確保防火墻沒有阻止 UDP 廣播:
sudo iptables -I INPUT -p udp --dport 8080 -j ACCEPT
sudo iptables -I OUTPUT -p udp --dport 8080 -j ACCEPT
組播(Multicast)編程指南(C 語言)
單播(Unicast):數據僅發送給一個特定的主機。
廣播(Broadcast):廣播方式發給所有的主機。過多的廣播會大量占用網絡帶寬,造成廣播風暴,影響正常的通信。
組播(Multicast):組播(又稱為多播)是一種折中的方式。只有加入某個多播組的主機才能收到數據。多播方式既可以發給多個主機,又能避免象廣播那樣帶來過多的負載(每臺主機要到傳輸層才能判斷廣播包是否要處理)
- 僅發送給加入 特定組播組 的主機。
- 減少網絡負載,避免廣播風暴。
- 適用于 IPTV、視頻流、在線游戲、股票行情推送等場景。
📌 組播地址
組播范圍:224.0.0.0 ~ 239.255.255.255
特殊組播地址:
224.0.0.1
:所有支持 IP 組播的主機。224.0.0.2
:所有路由器。
關鍵 API
函數/結構體 | 作用 |
---|---|
setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); | 加入組播組 |
setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); | 退出組播組 |
sendto(sockfd, message, len, 0, (struct sockaddr*)&addr, addr_len); | 發送組播 |
recvfrom(sockfd, buffer, len, 0, (struct sockaddr*)&addr, &addr_len); | 接收組播 |
struct ip_mreq | 組播配置結構體 |
組播發送端(C 語言實現)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define MULTICAST_GROUP "239.0.0.1" // 組播地址
#define MULTICAST_PORT 8080 // 組播端口
#define MESSAGE "🌍 這是組播消息!"int main() {int sockfd;struct sockaddr_in multicast_addr;int ttl = 64; // 組播生存時間(TTL)// 1?? 創建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("? 創建套接字失敗");exit(EXIT_FAILURE);}// 2?? 設置組播 TTLsetsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));// 3?? 設置組播地址memset(&multicast_addr, 0, sizeof(multicast_addr));multicast_addr.sin_family = AF_INET;multicast_addr.sin_port = htons(MULTICAST_PORT);multicast_addr.sin_addr.s_addr = inet_addr(MULTICAST_GROUP);printf("發送組播消息到 %s:%d\n", MULTICAST_GROUP, MULTICAST_PORT);while (1) {// 4?? 發送組播消息sendto(sockfd, MESSAGE, strlen(MESSAGE), 0,(struct sockaddr*)&multicast_addr, sizeof(multicast_addr));printf("? 發送消息: %s\n", MESSAGE);sleep(3); // 每 3 秒發送一次}close(sockfd);return 0;
}
組播接收端(C 語言實現)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define MULTICAST_GROUP "239.0.0.1" // 組播地址
#define MULTICAST_PORT 8080 // 組播端口
#define BUFFER_SIZE 1024int main() {int sockfd;struct sockaddr_in local_addr;struct ip_mreq mreq;char buffer[BUFFER_SIZE];socklen_t addr_len = sizeof(local_addr);// 1?? 創建 UDP 套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("? 創建套接字失敗");exit(EXIT_FAILURE);}// 2?? 允許端口復用(多個進程/設備可接收同一組播)int reuse = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));// 3?? 綁定本地地址memset(&local_addr, 0, sizeof(local_addr));local_addr.sin_family = AF_INET;local_addr.sin_port = htons(MULTICAST_PORT);local_addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {perror("? 綁定失敗");close(sockfd);exit(EXIT_FAILURE);}// 4?? 加入組播組mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);mreq.imr_interface.s_addr = htonl(INADDR_ANY);setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));printf("監聽組播消息 (%s:%d)...\n", MULTICAST_GROUP, MULTICAST_PORT);while (1) {// 5?? 接收組播消息memset(buffer, 0, BUFFER_SIZE);recvfrom(sockfd, buffer, BUFFER_SIZE, 0,(struct sockaddr*)&local_addr, &addr_len);printf("📩 收到消息: %s\n", buffer);}// 6?? 退出組播setsockopt(sockfd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));close(sockfd);return 0;
}
運行步驟:
- 編譯
gcc multicast_sender.c -o multicast_sender
gcc multicast_receiver.c -o multicast_receiver
- 運行組播接收端
./multicast_receiver
示例輸出:
監聽組播消息 (239.0.0.1:8080)...
收到消息: 🌍 這是組播消息!
收到消息: 🌍 這是組播消息!
- 運行組播發送端
./multicast_sender
示例輸出:
發送組播消息到 239.0.0.1:8080
? 發送消息: 🌍 這是組播消息!
? 發送消息: 🌍 這是組播消息!
編譯中可能遇到的問題:
🚨 1. recvfrom()
接收不到數據
? 解決方案
- 確保接收端 加入了相同的組播地址(239.0.0.1)。
- 使用
netstat -g
命令檢查當前加入的組播組:
netstat -g
- 確保 Linux 防火墻未阻止 UDP 組播:
sudo iptables -I INPUT -p udp --dport 8080 -j ACCEPT
sudo iptables -I OUTPUT -p udp --dport 8080 -j ACCEPT
組播是一種高效的網絡通信方式,可用于 IPTV、在線游戲、股票行情推送等應用場景。以上 C 代碼實現了 UDP 組播的發送和接收,支持多個設備同時接收組播消息。 相比廣播,組播能夠減少網絡負載,提高數據傳輸效率。
以上。僅供學習與分享交流,請勿用于商業用途!轉載需提前說明。
我是一個十分熱愛技術的程序員,希望這篇文章能夠對您有幫助,也希望認識更多熱愛程序開發的小伙伴。
感謝!