Linux网络编程- sockaddr & sockaddr_in & in_addr

struct sockaddr

struct sockaddr 是用于通用的套接字地址结构体,通常在多种网络API调用中被用作参数,尤其是在套接字编程中。它是许多具体套接字地址结构体(例如 struct sockaddr_in 用于IPv4、struct sockaddr_in6 用于IPv6)的超类或通用类型。

这个结构体的定义如下:

struct sockaddr {
    sa_family_t sa_family;    // 地址家族: AF_xxx 值
    char        sa_data[14];  // 存储特定协议的地址信息
};

字段解释:

  1. sa_family:

    • 数据类型: sa_family_t
    • 描述: 表示地址家族或协议族,常见的值为 AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(本地套接字),等等。
  2. sa_data:

    • 数据类型: char[14]
    • 描述: 一个通用的缓冲区,用于存储特定于协议的地址信息。其内容和长度对于每个地址家族都是不同的。

尽管 struct sockaddr 用作许多网络函数的参数,但在实践中,开发者通常会使用更具体的套接字地址结构体(如 sockaddr_insockaddr_in6),然后在调用函数时进行类型转换。这是因为 sockaddr 结构体并不提供足够的空间或适当的字段来存储所有类型的地址信息。

示例:

当我们调用 bind()connect()accept() 等函数时,可能会使用 struct sockaddr。例如:

struct sockaddr_in server_addr;
// ... (填充 server_addr 的字段)

// 使用 bind() 绑定套接字,注意类型转换
bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

在这里,尽管 bind() 函数期望一个 struct sockaddr * 参数,但我们可以传递一个 struct sockaddr_in *,只要进行适当的类型转换即可。这种模式在套接字编程中是非常常见的,因为 struct sockaddr 作为一个通用类型存在,而真正的信息存储在更具体的结构体中。

struct sockaddr_in

struct sockaddr_in 是一个用于表示 IPv4 地址的数据结构,它在 头文件中定义。它用于保存 IP 地址和端口号,并经常在各种网络编程任务中用于指定源和目的地址。

以下是 struct sockaddr_in 的主要字段和相关说明:

  1. sin_family:

    • 数据类型: sa_family_t
    • 描述: 这是一个地址家族(address family)。对于 IPv4 地址,它总是被设置为 AF_INET
  2. sin_port:

    • 数据类型: in_port_t
    • 描述: 16位的 TCP/UDP 端口号。它通常使用 htons() 函数转换为网络字节序。
  3. sin_addr:

    • 数据类型: struct in_addr
    • 描述: IPv4 地址。这是一个特定的结构,它只有一个字段 s_addr,该字段保存 IPv4 地址(以网络字节序)。
  4. sin_zero:

    • 数据类型: char sin_zero[8]
    • 描述: 此字段只是为了让 struct sockaddr_in 的大小与 struct sockaddr 相同,以确保兼容性。应该将其设置为零,并在实际使用中忽略它。

在网络编程中,struct sockaddr_in 结构体常用于如下任务:

  • 指定 bind() 要绑定的地址和端口
  • 告诉 connect() 函数要连接的远程地址和端口
  • 在 accept() 和 recvfrom() 函数中返回远程的地址和端口

一个常见的使用 struct sockaddr_in 的例子如下:

#include 
#include 
#include 
#include 

int main() {
    struct sockaddr_in address;
    memset(&address, 0, sizeof(address));

    address.sin_family = AF_INET;
    address.sin_port = htons(8080);  // 将端口号转换为网络字节序
    inet_pton(AF_INET, "127.0.0.1", &(address.sin_addr));  // 将字符串形式的 IP 转换为网络字节序的二进制形式

    // 打印 IP 和端口
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(address.sin_addr), ip, INET_ADDRSTRLEN);
    printf("IP: %s, Port: %d\n", ip, ntohs(address.sin_port));

    return 0;
}

上述程序运行结果为:

majn@tiger:~/C_Project/network_project$ ./sockaddr_in_demo 
IP: 127.0.0.1, Port: 8080

在这个例子中,我们创建了一个 struct sockaddr_in 结构体,设置了 IP 地址和端口,并打印了它们。


struct in_addr

struct in_addr 是一个数据结构,用于表示一个 IPv4 地址。这个结构体在 头文件中定义,通常与各种网络函数一起使用,特别是当处理 IPv4 地址时。

struct in_addr 本质上只包含一个字段:

struct in_addr {
    in_addr_t s_addr;  // IPv4 地址
};

字段说明:

  • s_addr: 一个 in_addr_t 类型,通常是一个 uint32_t,用于存储 IPv4 地址的网络字节序。

使用示例:

当我们使用如 inet_pton()inet_ntop() 这样的函数来处理 IPv4 地址时,通常会与 struct in_addr 互动:

#include 
#include 

int main() {
    const char *ip_string = "192.168.1.1";
    struct in_addr ip_addr;

    // 将 IP 字符串转换为二进制形式
    inet_pton(AF_INET, ip_string, &ip_addr);

    // 打印
    printf("s_addr (network byte order): 0x%x\n", ip_addr.s_addr);

    // 转换回字符串形式
    char converted[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &ip_addr, converted, INET_ADDRSTRLEN);
    printf("Converted back to string: %s\n", converted);

    return 0;
}

在此示例中,我们首先使用 inet_pton() 将 IPv4 地址的字符串形式转换为其二进制形式,并存储在 struct in_addr 中。然后,我们使用 inet_ntop() 将它转换回字符串形式。

这个结构体虽然简单,但在处理 IPv4 地址时,它是非常关键的,尤其是当与网络函数交互时。


uint32_t 是一个数据类型,表示一个无符号的 32 位整数。这种类型的变量可以存储从 0 到 4,294,967,295 (即 2^32 - 1) 的值。在 C 和 C++ 中,这个类型定义在标准头文件 中。

“uint32_t” 中的 “u” 代表“无符号”(unsigned),“int” 代表“整数”(integer),“32” 表示 32 位。

使用 uint32_t 而不是通常的 unsigned int 或其他数据类型的好处在于它明确地指定了变量的大小,无论编译器、操作系统或硬件平台。这可以确保跨平台代码的一致性和可移植性。

以下是如何使用 uint32_t 的简单示例:

#include 
#include   // 包含 uint32_t 的头文件

int main() {
    uint32_t number = 4294967295;  // 最大值
    printf("Value of number: %u\n", number);
    return 0;
}

程序输出如下:

majn@tiger:~/C_Project/network_project$ ./u32_int_demo 
Value of number: 4294967295

注意:使用 printf() 打印 uint32_t 时,使用 %u 格式说明符。

在一些特定的应用中,如网络编程、嵌入式编程和系统编程,数据的确切大小和不变性特别重要。在这些情况下,uint32_t 和其他固定宽度整数类型(如 int8_t, uint16_t 等)提供了确切的数据大小,有助于避免可能的移植性问题。

你可能感兴趣的:(工程化C,Linux,linux,网络)