struct sockaddr_in和struct sockaddr区别

本文详细介绍了在Linux环境下socket编程中使用的两种主要结构体:通用的sockaddr及Internet环境下专用的sockaddr_in。这两种结构体用于存储套接字地址,并且可以通过类型转换相互兼容。文章还解释了如何使用这些结构体来初始化socket并绑定地址。

结构体struct sockaddr在/usr/include/linux/socket.h中定义

struct sockaddr {
       unsigned short sa_family; /* 地址族, AF_xxx */
       char sa_data[14]; /* 14字节的协议地址*/
   };
  
上面是通用的socket地址,具体到Internet socket,用下面的结构,二者可以进行类型转换

结构体struct sockaddr_in在/usr/include/netinet/in.h中定义 
struct sockaddr_in {
       short int sin_family; /* 地址族 */
       unsigned short int sin_port; /* 端口号 */
       struct in_addr sin_addr; /* Internet地址 */
       unsigned char sin_zero[8]; /* 与struct sockaddr一样的长度 */
   };

 /* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */

 

struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。

 

这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。    


   struct in_addr就是32位IP地址。
   struct in_addr {
       unsigned long s_addr;
   };


你只要记住,填值的时候使用sockaddr_in结构,而作为函数的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。

 

也有
struct in_addr { 
union {   
struct { u_char s_b1,s_b2,s_b3,s_b4;} S_un_b;   
struct { u_short s_w1,s_w2;} S_un_w;   
u_long S_addr;
} S_un;
};
利用u_long htonl(u_long hostlong);将主机字节序转换为TCP/IP网络字节序.
利用u_short htons(u_short hostshort);将主机字节序转换为TCP/IP网络字节序.

inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。

 

--------------------------------------------------------------------------------------------------------------------------------------- 

通常的用法是:
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); /* 做一些错误检查! */

 

my_addr.sin_family = AF_INET; /* 主机字节序 */
my_addr.sin_port = htons(MYPORT); /* short, 网络字节序 */
my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");

 

bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */

//memset(&my_addr.sin_zero, 0, 8);

 

 

 

/* 不要忘了为bind()做错误检查: */
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

 

 

----------------------------------------------------------------------------------------------------

<think>我们被要求解释struct sockaddrstruct sockaddr_in区别、用途以及使用示例。 根据引用[1][2][3]: - struct sockaddr是一个通用的套接字地址结构,用于在套接字函数中作为参数类型。 - struct sockaddr_in是专门用于IPv4的套接字地址结构。 区别: 1. 定义包含的头文件: struct sockaddr 定义在 <sys/socket.h> 中,而 struct sockaddr_in 定义在 <netinet/in.h> 或 <arpa/inet.h> 中。 2. 结构体内容: struct sockaddr { sa_family_t sa_family; // 地址族,如AF_INET, AF_INET6等 char sa_data[14]; // 14字节的协议地址,包括IP地址端口号 }; struct sockaddr_in { short sin_family; // 地址族,通常为AF_INET unsigned short sin_port; // 端口号(网络字节序) struct in_addr sin_addr; // IPv4地址 char sin_zero[8]; // 填充,使sockaddr_insockaddr大小相同 }; struct in_addr { in_addr_t s_addr; // IPv4地址(32位,网络字节序) }; 3. 用途: - sockaddr是通用的,可以用于多种协议(如IPv4、IPv6、Unix域套接字等)。 - sockaddr_in是专门为IPv4设计的,方便程序员操作IPv4地址端口。 4. 使用方式: - 在编程中,我们通常先初始化一个sockaddr_in结构,然后在使用套接字函数(如bind, connect)时,将其强制转换为sockaddr*类型。 为什么需要两个结构? 因为套接字不仅支持IPv4,还支持IPv6(sockaddr_in6)Unix域套接字(sockaddr_un)等。为了统一接口,套接字函数(如bind, connect, accept等)使用通用的sockaddr*指针作为参数。这样,不同的协议族可以使用不同的结构,但都能通过强制转换来传递。 使用示例(IPv4 TCP服务器绑定端口): ```c #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { // 错误处理 } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; // 地址族:IPv4 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到任意网络接口 server_addr.sin_port = htons(8080); // 绑定端口8080 // 绑定:将sockaddr_in强制转换为sockaddr* if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { // 错误处理 } // ... 其他代码 return 0; } ``` 在IPv6中,使用sockaddr_in6: ```c struct sockaddr_in6 { sa_family_t sin6_family; // AF_INET6 in_port_t sin6_port; // 端口号(网络字节序) uint32_t sin6_flowinfo; // IPv6流信息 struct in6_addr sin6_addr; // IPv6地址 uint32_t sin6_scope_id; // 作用域ID }; ``` 另外,引用[4]提到了struct addrinfo,它是用于getaddrinfo函数返回的结果,其中ai_addr就是一个指向sockaddr结构的指针,可以指向sockaddr_in(IPv4)或sockaddr_in6(IPv6)。 注意:sockaddrsockaddr_in的大小都是16字节,这是为了保持兼容性(sockaddr_in中的sin_zero[8]就是为了填充到16字节)。同样,sockaddr_in6更大一些(28字节),但通过使用通用的sockaddr指针,可以传递任协议族的结构。 总结: - 使用sockaddr_in(或sockaddr_in6等)来设置具体的地址端口。 - 在调用套接字函数时,将其强制转换为sockaddr*类型。 因此,这两个结构体的区别主要在于:sockaddr是通用的,而sockaddr_in是专门为IPv4设计的。在实际编程中,我们使用sockaddr_in来填充IPv4地址信息,然后转换为sockaddr*传递给系统调用。 §§相关问题§§ 1. 如在IPv6编程中使用struct sockaddr_in6? 2. 什么是网络字节序?为什么在套接字编程中需要转换字节序? 3. 如使用struct addrinfo进行地址无关的编程? 4. 除了IPv4IPv6,还有哪些常见的套接字地址结构? 5. 在套接字编程中,如处理不同协议族的地址结构? </think>### `struct sockaddr` 与 `struct sockaddr_in` 详解 #### 一、核心区别对比 | **特性** | `struct sockaddr` (通用结构) | `struct sockaddr_in` (IPv4专用) | |------------------|---------------------------------------|----------------------------------------| | **定义位置** | `<sys/socket.h>` | `<netinet/in.h>` | | **地址存储方式** | 混合存储(`sa_data[14]`含IP+端口)[^1] | 分离存储(`sin_port`+`sin_addr`)[^3] | | **地址族标识** | `sa_family`(如 `AF_INET`) | `sin_family`(固定为 `AF_INET`) | | **内存大小** | 16字节 | 16字节(通过 `sin_zero[8]` 对齐)[^3] | | **主要用途** | 套接字函数的通用参数类型 | IPv4地址的专用操作 | #### 二、结构体定义解析 **1. `struct sockaddr`(通用结构)** ```c #include <sys/socket.h> struct sockaddr { sa_family_t sa_family; // 地址族标识(如 AF_INET/AF_INET6) char sa_data[14]; // 原始地址数据(IP+端口混合) }; ``` **缺陷**:地址端口混合在`sa_data`中,需手动解析[^1]。 **2. `struct sockaddr_in`(IPv4专用)** ```c #include <netinet/in.h> struct sockaddr_in { short sin_family; // 地址族(固定为 AF_INET) unsigned short sin_port; // 16位端口号(网络字节序) struct in_addr sin_addr; // 32位IPv4地址 char sin_zero[8]; // 填充字节(保证16字节对齐) }; struct in_addr { in_addr_t s_addr; // 32位IP地址网络字节序) }; ``` **优势**: - 端口(`sin_port`)IP(`sin_addr`)分离存储 - 支持直接数值操作(如 `inet_addr("192.168.1.1")`) #### 三、使用场景与转换关系 **1. 使用流程** ```mermaid graph LR A[创建socket] --> B[初始化sockaddr_in] B --> C[设置IP/端口] C --> D[强制转换为sockaddr*] D --> E[bind/connect等系统调用] ``` **2. 类型转换原理** 因二者内存大小相同(16字节),可通过指针强制转换: ```c struct sockaddr_in server_addr; // 填充IPv4地址信息 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); // 转换为通用类型后使用 bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); ``` **数学关系**: $$ \text{sizeof(sockaddr)} \equiv \text{sizeof(sockaddr_in)} = 16 \text{ bytes} $$ #### 三、完整使用示例(TCP服务器) ```c #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { // 1. 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 2. 初始化专用地址结构 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); // 端口 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有IP // 3. 转换为通用地址结构并绑定 bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 4. 监听连接 listen(sockfd, 5); // ... 后续accept等操作 } ``` #### 四、其他相关结构 1. **`struct sockaddr_in6`** (IPv6专用) ```c struct sockaddr_in6 { sa_family_t sin6_family; // AF_INET6 in_port_t sin6_port; // 端口 uint32_t sin6_flowinfo; // 流信息 struct in6_addr sin6_addr; // 128位IPv6地址 uint32_t sin6_scope_id; // 作用域ID }; ``` **大小**:28字节(大于`sockaddr`的16字节) 2. **`struct addrinfo`** (地址无关编程) ```c struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; // 指向sockaddr的通用指针[^4] }; ``` > **关键结论**:在实际编程中总是优先使用协议专用结构(如 `sockaddr_in`),仅在系统调用时转换为 `sockaddr*` 以保持接口统一性[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值