一、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 发送数据。
- 广播发送方需设置