struct sockaddr定义及延伸

本文探讨了struct sockaddr的定义,解析了__SOCKADDR_COMMON宏的用途,以及sa_family_t的数据类型。通过实例展示了如何根据需要在sockaddr结构中填充不同类型的地址,如sockaddr_in和sockaddr_un。此外,还解释了在UNIX域套接字中sun_family字段的含义和如何设置协议类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟踪看一下 strcut sockaddr 是怎么定义的,这个定义在 /usr/include/bits/socket.h里
引用

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);   
    char sa_data[14];          
  };

由上可见,sockaddr 结构体的第一个成员是一个宏定义,再来看一下这个宏定义(在/usr/include/bits/sockaddr.h 里):
引用


#define __SOCKADDR_COMMON(sa_prefix)sa_family_t sa_prefix##family

由注释可知,利用这个宏,我们根据需要,在 sockaddr 里可以填入不同的地址类型规范,如 struct sockaddr,struct sockaddr_in,struct sockaddr_un 等。

同样在  /usr/include/bits/sockaddr.h里看到上面的 sa_family_t 的原型是:
引用

typedef unsigned short int sa_family_t;

由此可见,是个无符号短整型。

例如,我们用的是 UNIX 域套接字,那么可以在 /usr/include/sys/un.h 中观察到 sockaddr_un的定义:
引用

struct sockaddr_un
  {
    __SOCKADDR_COMMON (sun_);
    char sun_path[108];        
  };

那么,根据上面的 __SOCKADDR_COMMON 的定义,可知结构体中的第一个成员为:

sun_family              

所以,这个结构题可改写为:
struct sockaddr_un
  {
    sa_family_t sun_family ;
    char sun_path[108];        
  };[/quote]
由于 sa_family 是无符号整型,而在 /usr/include/bits/socket.h 里定义的协议类型 (PF_XXX(实际上被重定义为 AF_XXX) 便可以赋给 sun_family,如 selfdefine_address.sun_family= AF_UNIX (表示使用 UNIX 域套接字)。
<think>我们正在讨论C++中的struct sockaddr_in结构体。根据引用[1],struct sockaddr_in的定义如下: ```c struct sockaddr_in { short sin_family; // 地址族,必须为AF_INET u_short sin_port; // 端口号(16位) struct in_addr sin_addr; // IPv4地址(32位) char sin_zero[8]; // 未使用,必须填充0 }; ``` 其中,`in_addr`结构体定义如下: ```c struct in_addr { unsigned long s_addr; // IPv4地址(网络字节序) }; ``` 在C++中,我们使用这个结构体来设置IPv4地址和端口号,以便进行网络编程(如TCP/UDP套接字编程)。 ### 使用步骤: 1. **包含头文件**: ```cpp #include <sys/socket.h> #include <netinet/in.h> // 包含sockaddr_in和in_addr #include <arpa/inet.h> // 包含inet_addr等转换函数 ``` 2. **创建并初始化结构体**: ```cpp struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); // 初始化为0 ``` 3. **设置地址族**: ```cpp server_addr.sin_family = AF_INET; // IPv4 ``` 4. **设置端口号**(注意使用网络字节序): ```cpp server_addr.sin_port = htons(8080); // 设置端口为8080,htons将主机字节序转换为网络字节序(16位) ``` 5. **设置IP地址**(字符串形式的IP地址转换为网络字节序): ```cpp // 方法1:使用inet_addr(将点分十进制字符串转换为32位网络字节序整数) server_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 方法2:使用inet_pton(推荐,支持IPv6) if (inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr) <= 0) { // 错误处理 } ``` 6. **使用结构体**: - 在`bind()`、`connect()`、`sendto()`等函数中,需要将`sockaddr_in`强制转换为`sockaddr*`类型: ```cpp int sockfd = socket(AF_INET, SOCK_STREAM, 0); bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); ``` ### 完整示例(TCP客户端连接): ```cpp #include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { // 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return 1; } // 设置服务器地址 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); // 服务器端口 if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) { perror("inet_pton"); close(sockfd); return 1; } // 连接服务器 if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("connect"); close(sockfd); return 1; } std::cout << "Connected to server!" << std::endl; close(sockfd); return 0; } ``` ### 注意事项: - **字节序转换**:端口号(16位)和IP地址(32位)必须使用网络字节序(大端序)。使用`htons`(host to network short)转换端口,`htonl`(host to network long)转换IP地址(但`inet_addr`和`inet_pton`已自动处理)。 - **清零结构体**:使用`memset`或`bzero`将结构体清零,避免未初始化字段导致的问题。 - **地址转换**:推荐使用`inet_pton`(presentation to network)代替`inet_addr`,因为它支持IPv6且更安全。 通过以上步骤,我们可以在C++中正确使用`sockaddr_in`结构体进行网络编程[^1]。 §§相关问题§§ 1. 如何将网络字节序的IP地址转换回字符串形式? 2. `sockaddr_in`和`sockaddr`有什么区别? 3. 在IPv6编程中应该使用什么结构体?</think>### C++ 中 `struct sockaddr_in` 的定义与使用 `struct sockaddr_in` 是用于 IPv4 地址处理的网络编程核心结构体,定义在 `<netinet/in.h>` 头文件中。以下是详细说明: #### 结构体定义(引用[1]) ```cpp struct sockaddr_in { short sin_family; // 地址族,必须为 AF_INET u_short sin_port; // 16位端口号(网络字节序) struct in_addr sin_addr; // 32位IP地址 char sin_zero[8]; // 填充字节(必须为0) }; struct in_addr { unsigned long s_addr; // 32位IPv4地址(网络字节序) }; ``` #### 核心字段说明 1. **`sin_family`**:地址族类型 ```cpp server_addr.sin_family = AF_INET; // IPv4协议 ``` 2. **`sin_port`**:端口号(必须使用网络字节序) ```cpp server_addr.sin_port = htons(8080); // 主机字节序转网络字节序 ``` 3. **`sin_addr`**:IP地址(支持多种赋值方式) ```cpp // 方法1:直接赋值(网络字节序) server_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 方法2:使用主机名解析(引用[2]) struct hostent* host = gethostbyname("example.com"); memcpy(&server_addr.sin_addr, host->h_addr, host->h_length); ``` #### 完整使用示例 ```cpp #include <iostream> #include <cstring> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { // 创建结构体实例 struct sockaddr_in server_addr; // 初始化为零 memset(&server_addr, 0, sizeof(server_addr)); // 设置地址族 server_addr.sin_family = AF_INET; // 设置端口(主机字节序 → 网络字节序) server_addr.sin_port = htons(8080); // 设置IP地址(字符串 → 网络字节序) server_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 在套接字操作中使用(如bind) int sockfd = socket(AF_INET, SOCK_STREAM, 0); bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 转换为可读格式 std::cout << "IP: " << inet_ntoa(server_addr.sin_addr) << " Port: " << ntohs(server_addr.sin_port) << std::endl; return 0; } ``` #### 关键注意事项 1. **字节序转换**: - 发送数据:`htons()`(主机→网络) - 接收数据:`ntohs()`(网络→主机) 2. **地址转换函数**: ```cpp inet_addr("192.168.1.1"); // 字符串→网络字节序(已弃用) inet_pton(AF_INET, "192.168.1.1", &addr); // 推荐替代方案 inet_ntoa(server_addr.sin_addr); // 网络字节序→字符串 ``` 3. **结构体清零**: ```cpp memset(&server_addr, 0, sizeof(server_addr)); // 避免未初始化错误 ``` 4. **类型转换**: ```cpp // 在套接字函数中需要强制转换 connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); ``` > 此结构体是TCP/UDP编程的基础,在创建服务器或客户端时都需要使用(参考TCP示例[^3])。实际使用时需包含头文件:`#include <netinet/in.h>`[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值