结构体struct sockaddr sockaddr_in

本文深入剖析了Linux环境下用于描述套接字地址的sockaddr与sockaddr_in结构,详细解释了它们的定义、组成部分及其在socket编程中的应用。重点介绍了如何在实际编程中正确使用这两个结构体,包括初始化、参数传递以及与其他网络相关函数的配合使用。

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

http://www.cnblogs.com/hnrainll/archive/2011/04/24/2026432.html


在linux环境下,结构体struct sockaddr在/usr/include/linux/socket.h中定义,具体如下:
typedef unsigned short sa_family_t;
struct sockaddr {
        sa_family_t     sa_family;    /* address family, AF_xxx       */
        char            sa_data[14];    /* 14 bytes of protocol address */

在linux环境下,结构体struct sockaddr_in在/usr/include/netinet/in.h中定义,具体如下:
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
    __SOCKADDR_COMMON (sin_);
    in_port_t sin_port;                     /* Port number. */
    struct in_addr sin_addr;            /* Internet address. */

    /* Pad to size of `struct sockaddr'. */
    unsigned char sin_zero[sizeof (struct sockaddr) -
                           __SOCKADDR_COMMON_SIZE -
                           sizeof (in_port_t) -
                           sizeof (struct in_addr)];     
                           /* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */
};
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。

下面是struct sockaddr_in中用到两个数据类型,具体定义如下:
/* Type to represent a port. */
typedef uint16_t in_port_t;
 

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

BSD网络软件中包含了两个函数,用来在二进制地址格式和点分十进制字符串格式之间相互转换,但是这两个函数仅仅支持IPv4。
       in_addr_t inet_addr(const char *cp);
       char *inet_ntoa(struct in_addr in);
功能相似的两个函数同时支持IPv4和IPv6
       const char *inet_ntop(int domain, const void *addr, char *str, socklen_t size);
       int inet_pton(int domain, const char *str, void *addr);

通常的用法是:
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(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

#define UNIX_PATH_MAX 108

  struct sockaddr_un {

  sa_family_t sun_family; /*PF_UNIX或AF_UNIX */

  char sun_path[UNIX_PATH_MAX]; /* 路径名 */

  };

struct sockaddr结构类型是用来保存socket信息的:
   struct sockaddr {
   unsigned short sa_family; /* 地址族, AF_xxx */——地址的格式
  char sa_data[14]; /* 14 字节的协议地址 */——地址值(IP和端口号)
  };

Sockfd是调用socket函数返回的socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。 
  struct sockaddr结构类型是用来保存socket信息的: 
  struct sockaddr { 
   unsigned short sa_family; /* 地址族, AF_xxx */ 
char sa_data[14]; /* 14 字节的协议地址 */ 
}; 
  sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号。 
  另外还有一种结构类型: 
  struct sockaddr_in { 
   short int sin_family; /* 地址族 */ 
   unsigned short int sin_port; /* 端口号 */ 
   struct in_addr sin_addr; /* IP地址 */ 
   unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */ 
  }; 
   这个结构更方便使用。sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向 sockaddr_in的指针转换为指向sockaddr的指针;或者相反。


你只要记住,填值的时候使用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 };

<think>好的,我需要详细解释structsockaddr_instruct 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]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值