sockaddr与sockaddr_in,sockaddr_un结构体详细讲解

本文介绍了socket编程中的核心数据结构sockaddr及其派生结构sockaddr_in和sockaddr_un,详细解释了这些结构的作用及如何在实际编程中应用。此外还提供了网络字节与主机字节之间的转换方法。

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

struct sockaddr {
unsigned short sa_family;     /* address family, AF_xxx */
char sa_data[14];                 /* 14 bytes of protocol address */
};
sa_family是地址家族,一般都是“AF_xxx”的形式。好像通常大多用的是都是AF_INET。
sa_data是14字节协议地址。
此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。

但一般编程中并不直接针对此数据结构操作,而是使用另一个与sockaddr等价的数据结构
sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
short int sin_family;                      /* Address family */
unsigned short int sin_port;       /* Port number */
struct in_addr sin_addr;              /* Internet address */
unsigned char sin_zero[8];         /* Same size as struct sockaddr */
};


struct in_addr {
unsigned long s_addr;
};


typedef struct in_addr {
union {
            struct{
                        unsigned char s_b1,
                        s_b2,
                        s_b3,
                        s_b4;
                        } S_un_b;
           struct {
                        unsigned short s_w1,
                        s_w2;
                        } S_un_w;
            unsigned long S_addr;
          } S_un;
} IN_ADDR;


sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr按照网络字节顺序存储IP地址

sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向
sockadd的结构体,并代替它。也就是说,你可以使用sockaddr_in建立你所需要的信息,
在最后用进行类型转换就可以了bzero((char*)&mysock,sizeof(mysock));//初始化
mysock结构体名
mysock.sa_family=AF_INET;
mysock.sin_addr.s_addr=inet_addr("192.168.0.1");
……
等到要做转换的时候用:
(struct sockaddr*)mysock

添加点关于sockaddr_un结构体的介绍:
sockaddr_un

Unix socket address record.

Declaration

Source position: socketsh.inc line 152

type sockaddr_un = packed record

   sun_family: sa_family_t;//sa_family_t的类型是WORD,即unsiged short

Address family

   sun_path: array [0..107] of Char;

File name

end;

Description

sockaddr_un is used to store a UNIX socket addres for the Bind, Recv and Send calls.


一般关心的是如何把网络中的地址转化成字符串的形式。用到的函数可以是

char * inet_ntoa(struct in_addr in); 以将in_addr类型的IP地址转换为字符串形式IP地址。

基本步骤是获得网络接口地址pcap_addr中的sockaddr *addr,然后强制类型转化成sockaddr_in。再获得其中的struct  in_addr sin_addr,这个网络字节存储的IP地址,最后调用函数inet_ntoa,完成转换。

常用字节转化函数:

u_long htonl(u_long hostlong):将主机字节序转换为TCP/IP网络字节序.

u_short htons(u_short hostshort):将主机字节序转换为TCP/IP网络字节序.

unsigned long in inet_addr(const char *cp):将一个点分制的IP地址(如192.168.0.1)转换为网络二进制数字

int inet_aton(cont char* cp, struct in_addr *inp):将网络地址转为网络二进制数字,与inet_addr的区别是,结果不是作为返回值,而是保存形参inp所指的in_addr结构体中

char *inet_ntoa(struct in_addr in):将网络二进制数字转为网络地址


<think>好的,我需要详细解释structsockaddr_in和struct sockaddr这两个结构体,以及它们在C网络编程中的应用。首先,我需要回顾用户提供的引用内容,确保信息准确。根据引用[4],structsockaddr是一个通用的地址结构,用于不同协议族的API参数。而structsockaddr_in则是IPv4专用的地址结构。用户的问题是要详细说明这两个结构体,所以我需要分点解释它们的定义、成员变量以及用法。接下来,引用[2]提到sockaddr_in需要先赋值再传入bind函数,成员包括sin_family、sin_port和sin_addr。需要详细说明每个成员的作用,比如sin_family是地址族,sin_port是端口号,使用网络字节序。sin_addr是struct in_addr类型,包含s_addr成员,如引用[1]所示,它是一个32位的IPv4地址。引用[3]提到structaddrinfo中的ai_addr指向填充的套接字地址结构,这可能getaddrinfo函数有关,但用户的问题集中在sockaddrsockaddr_in,所以可能需要简要提及协议无关编程中的structsockaddr_storage,但重点还是放在IPv4的结构上。用户还提到需要参考之前的回答,即关于connect函数的解析。在之前的回答中,已经涉及了sockaddr_in的使用示例,这里可以呼应,说明这两个结构体在connect、bind等函数中的具体应用。 需要注意结构体转换的部分,如引用[4]所述,将sockaddr_in指针强制转换为sockaddr指针传入系统调用。这一点很重要,需要强调类型转换的必要性,因为函数参数需要通用结构体指针。另外,需要解释网络字节序和主机字节序的转换,如htons和htonl函数的使用,这在赋值sin_port和sin_addr.s_addr时是必须的,如引用[2]中的示例。 最后,生成相关问题时要围绕结构体的使用、转换、成员变量作用,以及相关函数如bind、connect的关系展开。确保问题有助于用户深入理解结构体在网络编程中的角色。</think>### `struct sockaddr` `struct sockaddr_in` 结构体详解 #### 一、`struct sockaddr` 通用地址结构 ##### 1. 定义作用 ```c #include <sys/socket.h> struct sockaddr { sa_family_t sa_family; // 地址族(如AF_INET/AF_INET6) char sa_data[14]; // 协议地址(含IP+端口) }; ``` - **核心作用**:作为网络编程API的通用参数类型,屏蔽不同协议族的差异[^4] - **典型应用场景**:`bind()`, `connect()`, `accept()`等函数的参数类型 ##### 2. 设计特点 - **通用容器**:`sa_data`字段存储具体协议地址的二进制数据 - **强制类型转换**:实际使用时需转换为具体协议族的结构体指针(如`sockaddr_in*`) #### 二、`struct sockaddr_in` IPv4专用结构 ##### 1. 定义解析 ```c #include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; // 地址族(必须为AF_INET) in_port_t sin_port; // 16位端口号(网络字节序) struct in_addr sin_addr; // 32位IPv4地址 char sin_zero[8]; // 填充字段(保持sockaddr大小一致) }; struct in_addr { uint32_t s_addr; // 32位IPv4地址(网络字节序)[^1] }; ``` ##### 2. 成员详解 | 成员 | 类型 | 说明 | |--------------|-----------------|----------------------------------------------------------------------| | `sin_family` | `sa_family_t` | 必须设置为`AF_INET`表示IPv4协议族 | | `sin_port` | `in_port_t` | 端口号(需用`htons()`转换为主机到网络的字节序)[^2] | | `sin_addr` | `struct in_addr`| IPv4地址(可用`inet_pton()`或`htonl(INADDR_ANY)`赋值)[^2] | | `sin_zero` | `char[8]` | 保留字段,通常置零 | ##### 3. 典型初始化示例 ```c struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); // 清空结构体 serv_addr.sin_family = AF_INET; // IPv4协议族 serv_addr.sin_port = htons(8080); // 设置端口(主机序→网络序) inet_pton(AF_INET, "192.168.1.100", &serv_addr.sin_addr); // 设置IP地址 ``` #### 三、结构体转换原理 ```c // 类型转换示例 struct sockaddr_in ipv4_addr; struct sockaddr *generic_addr = (struct sockaddr*)&ipv4_addr; // 强制转换[^4] // 在bind函数中的使用 bind(sockfd, generic_addr, sizeof(ipv4_addr)); ``` - **内存对齐**:`sockaddr_in``sockaddr`的前两个字节都存储地址族类型 - **大小保证**:`sockaddr_in`的`sin_zero`填充确保`sockaddr`大小相同(16字节) #### 四、协议无关编程扩展 ```c struct sockaddr_storage { // 兼容IPv4/IPv6的大结构体 sa_family_t ss_family; char __ss_padding[128]; }; ``` - **设计目的**:替代`sockaddr`处理不同协议族的地址结构(如IPv6的`sockaddr_in6`)[^3] - **使用场景**:需要同时支持IPv4/IPv6的服务器程序 --- ### 应用对比表 | 特性 | `sockaddr` | `sockaddr_in` | |--------------------|-----------------------|-----------------------| | **协议族支持** | 通用容器 | 专用于IPv4 | | **地址存储方式** | 原始二进制数据 | 结构化字段存储 | | **典型API参数类型**| 函数参数类型 | 实际填充类型 | | **内存占用** | 16字节 | 16字节 | | **扩展性** | 依赖强制类型转换 | 直接操作结构化成员 | --- ### 代码示例(TCP服务器绑定) ```c #include <sys/socket.h> #include <netinet/in.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(8888); // 端口转换网络字节序[^2] serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定所有网卡[^2] // 结构体指针转换后绑定 bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(sockfd, 5); return 0; } ``` --- ### 相关问题 1. 如何正确处理`sockaddr_in6`结构体实现IPv6支持? 2. `inet_pton()``inet_addr()`在IP地址转换时有何区别? 3. 为什么网络编程中必须进行字节序转换? 4. 如何通过`getsockname()`获取已绑定套接字的地址信息? 5. `sockaddr_storage`结构体在协议无关编程中的具体应用场景有哪些?[^3]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值