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

本文详细介绍了套接字编程中的核心数据结构sockaddr及其派生结构sockaddr_in和sockaddr_un。sockaddr用于指定bind、connect等函数的地址信息,sockaddr_in专为IPv4设计,而sockaddr_un用于本地UNIX套接字通信。

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


    进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式。这样做可以避免为黑客留下后门。


创建
    使用套接字函数socket创建,不过传递的参数与网络套接字不同。域参数应该是PF_LOCAL或者PF_UNIX,而不能用PF_INET之类。本地套接字的通讯类型应该是SOCK_STREAM或SOCK_DGRAM,协议为默认协议。例如:

int sockfd; 
sockfd = socket(PF_LOCAL, SOCK_STREAM, 0);


绑定
    创建了套接字后,还必须进行绑定才能使用。不同于网络套接字的绑定,本地套接字的绑定的是struct sockaddr_un结构。struct sockaddr_un结构有两个参数:sun_family、sun_path。sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路径。通常将文件放在/tmp目录下。例如:

struct sockaddr_un sun;
sun.sun_family = AF_LOCAL;
strcpy(sun.sun_path, filepath);
bind(sockfd, (struct sockaddr*)&sun, sizeof(sun));

监听
    本地套接字的监听、接受连接操作与网络套接字类似。

连接
    连接到一个正在监听的套接字之前,同样需要填充struct sockaddr_un结构,然后调用connect函数。

    连接建立成功后,我们就可以像使用网络套接字一样进行发送和接受操作了。甚至还可以将连接设置为非阻塞模式,这里就不赘述了。

<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]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值