一、setsockopt/getsockopt 函數詳解
1. 函數原型
c
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
2. 功能
- setsockopt:設置套接字選項(修改網絡屬性)。
- getsockopt:獲取套接字選項(查詢網絡屬性)。
3. 參數說明
參數 | 描述 |
---|---|
sockfd | 套接字文件描述符 |
level | 協議層級:SOL_SOCKET (通用選項)IPPROTO_IP (IP 層選項)IPPROTO_UDP (UDP 層選項) |
optname | 選項名稱(依賴于level ) |
optval | 選項值(指針類型,具體類型取決于optname ) |
optlen | optval 的字節長度(getsockopt 需傳入指針,setsockopt 需傳入值) |
4. 常用選項及示例
4.1 SOL_SOCKET 層級
SO_REUSEADDR
:允許重用本地地址和端口(解決端口占用問題)。c
int optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
SO_BROADCAST
:允許發送廣播數據(僅 UDP 可用)。c
int optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); // 發送方需設置
SO_RCVTIMEO/SO_SNDTIMEO
:設置接收 / 發送超時時間(struct timeval
類型)。c
struct timeval timeout = {3, 0}; // 3秒超時 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
4.2 IPPROTO_IP 層級(組播相關)
IP_ADD_MEMBERSHIP
:加入組播組。c
struct ip_mreqn mreq; mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); // 組播IP mreq.imr_address.s_addr = inet_addr("192.168.1.100"); // 本地IP mreq.imr_ifindex = 0; // 網絡接口索引(0表示自動選擇) setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
IP_DROP_MEMBERSHIP
:退出組播組(參數同IP_ADD_MEMBERSHIP
)。
4.3 IPPROTO_TCP 層級
TCP_NODELAY
:禁用 Nagle 算法(提升實時性)。c
int optval = 1; setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
二、UDP 多點通信
1. 廣播通信(Broadcast)
-
特點:
- 一對多通信,數據發送到局域網內所有主機。
- 廣播地址示例:
192.168.1.255
(網絡號 + 全 1 主機號)或255.255.255.255
(受限廣播)。 - 僅 UDP 支持,數據不可跨路由器。
-
實現步驟:
廣播接收者(UDP 服務器):
- 創建 UDP 套接字:
socket(AF_INET, SOCK_DGRAM, 0)
。 - 綁定廣播地址(如
192.168.1.255
或0.0.0.0
)。c
struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8888); addr.sin_addr.s_addr = inet_addr("192.168.1.255"); bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
- 接收數據:
recvfrom()
。
廣播發送者(UDP 客戶端):
- 創建 UDP 套接字。
- 設置
SO_BROADCAST
選項。 - 發送數據到廣播地址:
sendto()
。c
int optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(8888); dest_addr.sin_addr.s_addr = inet_addr("192.168.1.255"); sendto(sockfd, "Hello", 5, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
- 創建 UDP 套接字:
2. 組播通信(Multicast)
-
特點:
- 一對一組通信,僅加入組播組的主機可接收數據。
- 組播 IP 范圍:
224.0.0.0
~239.255.255.255
。 - 需通過
IP_ADD_MEMBERSHIP
加入組播組。
-
實現步驟:
組播接收者(UDP 服務器):
- 創建 UDP 套接字。
- 加入組播組:
setsockopt()
設置IP_ADD_MEMBERSHIP
。 - 綁定組播 IP 和端口(或綁定
0.0.0.0
接收所有組播數據)。c
struct ip_mreqn mreq; mreq.imr_multiaddr.s_addr = inet_addr("224.1.2.3"); mreq.imr_address.s_addr = INADDR_ANY; // 本地IP設為任意 setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8888); addr.sin_addr.s_addr = INADDR_ANY; // 綁定任意IP bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
組播發送者(UDP 客戶端):
- 創建 UDP 套接字。
- 直接發送數據到組播 IP:
sendto()
。c
struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(8888); dest_addr.sin_addr.s_addr = inet_addr("224.1.2.3"); sendto(sockfd, "Hello", 5, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
三、關鍵對比
特性 | 廣播(Broadcast) | 組播(Multicast) |
---|---|---|
地址范圍 | 局域網內所有主機(如192.168.1.255 ) | 組播組(如224.1.2.3 ) |
網絡層支持 | 依賴鏈路層廣播 | 依賴 IP 組播協議 |
跨路由 | 不可跨路由 | 可通過配置跨路由(需路由器支持) |
資源消耗 | 高(所有主機接收) | 低(僅組成員接收) |
適用場景 | 局域網內通知(如 DHCP) | 實時流媒體、在線會議(如視頻直播) |
四、代碼示例:UDP 廣播聊天
1. 廣播接收者(服務器)
c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>#define PORT 8888
#define BUF_SIZE 128int main() {// 創建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) { perror("socket error"); return -1; }// 允許端口重用int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));// 綁定廣播地址struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(PORT);addr.sin_addr.s_addr = inet_addr("192.168.1.255"); // 或 INADDR_BROADCASTif (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {perror("bind error"); return -1;}// 接收廣播數據char buf[BUF_SIZE] = {0};struct sockaddr_in cli_addr;socklen_t cli_len = sizeof(cli_addr);while (1) {ssize_t n = recvfrom(sockfd, buf, BUF_SIZE-1, 0, (struct sockaddr*)&cli_addr, &cli_len);if (n > 0) {buf[n] = '\0';printf("Received: %s\n", buf);}}close(sockfd);return 0;
}
2. 廣播發送者(客戶端)
c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>#define PORT 8888
#define BUF_SIZE 128int main() {// 創建UDP套接字int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) { perror("socket error"); return -1; }// 允許發送廣播int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval));// 廣播地址struct sockaddr_in dest_addr;memset(&dest_addr, 0, sizeof(dest_addr));dest_addr.sin_family = AF_INET;dest_addr.sin_port = htons(PORT);dest_addr.sin_addr.s_addr = inet_addr("192.168.1.255");// 發送數據char buf[BUF_SIZE] = {0};while (fgets(buf, BUF_SIZE, stdin)) {buf[strcspn(buf, "\n")] = 0; // 去除換行符sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));memset(buf, 0, BUF_SIZE);}close(sockfd);return 0;
}
五、總結
setsockopt/getsockopt
:是網絡編程中配置套接字行為的核心函數,通過不同層級和選項可實現端口重用、超時控制、組播加入等高級功能。- UDP 廣播:適用于局域網內的一對多通信,但存在廣播風暴風險,且無法跨路由。
- UDP 組播:通過組播組實現高效的一對多通信,節省網絡資源,適合實時數據傳輸。
- 注意事項:
- 廣播發送方需設置
SO_BROADCAST
選項,接收方需綁定廣播地址。 - 組播接收方需通過
IP_ADD_MEMBERSHIP
加入組播組,發送方直接向組播 IP 發送數據。
- 廣播發送方需設置