请求总结socket地址结构联合体形式及所用具体地址结构细节
Socket地址结构联合体完整概述
一、联合体的核心作用
Socket API的多数核心函数(如bind()、connect()、recvfrom())要求传入通用的struct sockaddr指针,但不同协议族(IPv4、IPv6、Unix域等)的地址格式差异极大。用联合体封装各类地址结构,既能满足API对通用指针的要求,又能直接访问特定协议的字段,同时节省内存空间。
二、标准联合体的典型形式
以下是C语言中最常用的Socket地址联合体定义,包含主流协议族的地址结构:
#include <sys/socket.h> #include <netinet/in.h> #include <sys/un.h> union sockaddr_union { struct sockaddr sa; // 通用地址结构(API接口用) struct sockaddr_in sin; // IPv4专属地址结构 struct sockaddr_in6 sin6; // IPv6专属地址结构 struct sockaddr_un sun; // Unix域套接字专属地址结构 };
使用时,通过sa.sa_family字段判断地址族,再直接访问对应联合体成员即可。
三、各成员对应的详细地址结构
1. 通用地址结构 struct sockaddr
这是所有Socket地址结构的基础模板,仅作为API的通用接口使用:
struct sockaddr { sa_family_t sa_family; // 地址族标识(如AF_INET、AF_INET6、AF_UNIX) char sa_data[14]; // 可变长度地址数据,不同协议族格式不同 };
注意:sa_data长度仅14字节,无法容纳IPv6或Unix域地址,实际业务中需通过联合体转换为具体结构。
2. IPv4地址结构 struct sockaddr_in
专门用于IPv4协议的地址结构:
struct sockaddr_in { sa_family_t sin_family; // 固定为AF_INET in_port_t sin_port; // 端口号,需用`htons()`转换为网络字节序 struct in_addr sin_addr; // IPv4地址结构体 char sin_zero[8]; // 填充字段,使结构体总大小与`struct sockaddr`一致,通常用`memset`清零 }; // 嵌套的IPv4地址结构体 struct in_addr { uint32_t s_addr; // 32位IPv4地址,需用`htonl()`或`inet_addr()`转换为网络字节序 };
3. IPv6地址结构 struct sockaddr_in6
专门用于IPv6协议的地址结构:
struct sockaddr_in6 { sa_family_t sin6_family; // 固定为AF_INET6 in_port_t sin6_port; // 端口号,网络字节序 uint32_t sin6_flowinfo; // 流信息,用于QoS控制,通常设为0 struct in6_addr sin6_addr; // IPv6地址结构体 uint32_t sin6_scope_id; // 作用域ID,使用链路本地地址时需指定对应网络接口索引 }; // 嵌套的IPv6地址结构体 struct in6_addr { unsigned char s6_addr[16]; // 128位IPv6地址,网络字节序 };
4. Unix域套接字地址结构 struct sockaddr_un
用于同一主机内的进程间通信:
struct sockaddr_un { sa_family_t sun_family; // 固定为AF_UNIX(或AF_LOCAL) char sun_path[108]; // Unix域套接字的文件系统路径,如"/tmp/my_socket" };
注意:使用后需手动删除sun_path对应的文件,避免下次绑定失败。
四、联合体的实用示例
以recvfrom()接收多协议数据为例,用联合体统一处理不同地址类型:
union sockaddr_union peer_addr; socklen_t addr_len = sizeof(peer_addr); char buf[1024]; ssize_t recv_len = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&peer_addr, &addr_len); if (recv_len == -1) { perror("recvfrom"); exit(EXIT_FAILURE); } // 根据地址族分支处理 switch (peer_addr.sa.sa_family) { case AF_INET: printf("来自IPv4:%s:%d\n", inet_ntoa(peer_addr.sin.sin_addr), ntohs(peer_addr.sin.sin_port)); break; case AF_INET6: char ipv6_buf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &peer_addr.sin6.sin6_addr, ipv6_buf, sizeof(ipv6_buf)); printf("来自IPv6:%s:%d\n", ipv6_buf, ntohs(peer_addr.sin6.sin_port)); break; case AF_UNIX: printf("来自Unix域套接字:%s\n", peer_addr.sun.sun_path); break; }
内容的提问来源于stack exchange,提问作者Ingo
相关产品推荐
相关产品推荐

