sockaddr与sockaddr_in,sockaddr_un结构体详解

本文详细介绍了socket编程中使用的地址结构sockaddr、sockaddr_in及其成员变量的作用,同时还包括了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结构体的介绍:
sockaddr_un

Unix socket address record.用于本地Unix域通信

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>我们被要求解释struct sockaddr和struct 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. 除了IPv4和IPv6,还有哪些常见的套接字地址结构? 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]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值