网络编程中常用的结构体

1. sockaddr 结构体

sockaddr 结构体是用于表示各种类型的套接字地址的通用数据结构。在网络编程中,特别是使用套接字 API 时,sockaddr 是一个基础结构体。虽然它本身通常不直接使用,但许多其他具体的地址结构体都是基于它的。

struct sockaddr {
    unsigned short sa_family;    // 地址族,例如 AF_INET, AF_INET6 等
    char sa_data[14];            // 套接字地址数据,具体内容依地址族而定
};

主要成员说明

  1. sa_family: 表示地址的类型(地址族)。常见的地址族包括:

    • AF_INET:表示 IPv4 地址
    • AF_INET6:表示 IPv6 地址
  2. sa_data: 包含套接字地址数据,其具体内容取决于 sa_family 的值。例如,对于 AF_INET 地址族,sa_data 包含 IP 地址和端口号等信息。

存放协议族、端口和地址信息,客户端和connect()函数和服务端的bind()函数需要这个结构体。

2. sockaddr_in(用于 IPv4 地址)

sockaddr结构体是为了统一地址结构的表示方法,统一接口函数,但是,操作不方便,所以定义了等价的sockaddr_in结构体,它的大小与sockaddr相同,可以强制转换成sockaddr。

struct sockaddr_in {
    short int sin_family;        // 地址族(应设置为 AF_INET)
    unsigned short int sin_port; // 端口号(需要使用 htons() 函数将主机字节序转换为网络字节序)
    struct in_addr sin_addr;     // IP 地址(使用 in_addr 结构体表示)
    unsigned char sin_zero[8];   // 填充字节,使得结构体大小与 sockaddr 一致
};

struct in_addr {
    unsigned long s_addr;        // IPv4 地址(以网络字节序表示)
};

主要成员说明

  1. sin_family: 地址族,应该设置为 AF_INET,表示这是一个 IPv4 地址。

  2. sin_port: 端口号,使用 htons() 函数将主机字节序转换为网络字节序。例如:

 address.sin_port = htons(8080);
  1. sin_addr: IP 地址,使用 in_addr 结构体表示,s_addr 成员包含实际的 IPv4 地址,以网络字节序表示。可以使用 inet_addr() 函数将点分十进制字符串转换为 in_addr 结构体。例如:
address.sin_addr.s_addr = inet_addr("192.168.1.1");
  1. ** sin_zero **: 填充字节,使得 sockaddr_in 结构体的大小与sockaddr结构体一致。通常设置为全零,可以使用 memset 函数进行初始化:
memset(address.sin_zero, 0, sizeof(address.sin_zero));

示例代码
以下是一个使用 sockaddr_in 结构体来设置 IPv4 地址的示例代码:

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(8080); // 设置端口号,使用 htons() 转换为网络字节序
    address.sin_addr.s_addr = inet_addr("192.168.1.1"); // 设置 IP 地址
    memset(address.sin_zero, 0, sizeof(address.sin_zero)); // 填充 sin_zero

    printf("IPv4 Address: %s\n", inet_ntoa(address.sin_addr));
    printf("Port: %d\n", ntohs(address.sin_port));

    return 0;
}

3. gethostbyname 函数

gethostbyname 函数用于通过主机名获取对应的主机信息,特别是获取主机的 IP 地址。在网络编程中,这个函数通常用于将人类可读的主机名转换为网络可用的 IP 地址。

函数原型

#include <netdb.h>

struct hostent *gethostbyname(const char *name);

函数返回值

gethostbyname 返回一个指向 hostent 结构体的指针,该结构体包含有关主机的信息。如果调用失败,则返回 NULL。

hostent 结构体

hostent 结构体定义如下:

struct hostent {
    char *h_name;        // 官方主机名
    char **h_aliases;    // 主机别名列表
    int h_addrtype;      // 地址类型(通常是 AF_INET)
    int h_length;        // 地址长度
    char **h_addr_list;  // 地址列表(网络字节序)
};

hostent 结构体成员说明

  • h_name: 指向主机的正式名称的指针。
  • h_aliases: 一个指向指针数组的指针,这些指针指向主机的别名列表。列表以 NULL 结尾。
  • h_addrtype: 地址类型。通常是 AF_INET(IPv4 地址)或 AF_INET6(IPv6 地址)。
  • h_length: 地址长度,对于 IPv4 地址通常是 4 字节。
  • h_addr_list: 一个指向指针数组的指针,这些指针指向网络字节序的 IP 地址列表。列表以 NULL 结尾。可以使用 h_addr 宏来访问第一个地址。

4. 字符串IP与大端序IP的转换

在网络编程中,经常需要在字符串形式的IP地址(如 “192.168.1.1”)和大端序(网络字节序)的二进制格式之间进行转换。

C语言提供了几个库函数,用于字符串格式的IP和大端序IP的互相转换,用于网络通讯的服务端程序中。

typedef unsigned int in_addr_t;    // 32位大端序的IP地址。

// 把字符串格式的IP转换成大端序的IP,转换后的IP赋给sockaddr_in.in_addr.s_addr。

in_addr_t inet_addr(const char *cp); 

// 把字符串格式的IP转换成大端序的IP,转换后的IP将填充到sockaddr_in.in_addr成员。

int inet_aton(const char *cp, struct in_addr *inp);	

// 把大端序IP转换成字符串格式的IP,用于在服务端程序中解析客户端的IP地址。

char *inet_ntoa(struct in_addr in);

注意事项

  1. 地址族匹配:

    • 在使用 inet_ptoninet_ntop 时,要确保 af 参数与目标地址族匹配(例如,IPv4 使用 AF_INET,IPv6 使用 AF_INET6)。
    • 例如,试图用 AF_INET 处理 IPv6 地址会导致失败,反之亦然。
  2. 缓冲区大小:

    • inet_ntop 函数中的 dst 缓冲区必须足够大,以容纳转换后的字符串形式的IP地址。
    • 对于 IPv4 地址,建议使用 INET_ADDRSTRLEN;对于 IPv6 地址,建议使用 INET6_ADDRSTRLEN
  3. 错误检查:

    • 检查函数返回值,确保转换成功。
    • 对于 inet_atoninet_pton,返回值为 0 表示转换失败。
    • 对于 inet_ntoainet_ntop,返回值为 NULL 表示转换失败。
  4. 线程安全:

    • inet_ntoa 返回的是一个指向静态缓冲区的指针,不是线程安全的。多线程程序中应使用 inet_ntop 替代。
    • inet_ptoninet_ntop 都是线程安全的。
  5. 内存分配:

    • inet_ntoa 返回的指针指向的字符串不需要手动释放,因为它指向的是静态缓冲区。
    • 使用 inet_ntop 时,确保 dst 缓冲区是预先分配的,并且有足够的大小。
  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值