网络编程中结构体的区分

sockaddr结构体和sockaddr_in结构体

1
2
3
4
struct sockaddr {
unsigned short sa_family; // 协议族 (AF_INET, AF_INET6, etc.)
char sa_data[14]; // 地址数据 (协议相关)
};

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

sockaddr 结构体是为了统一地址结构的表示方法,统一接口函数,但是这个结构体并不方便使用,因此定义了等价的 sockaddr_in 结构体,它的大小和 sockaddr 结构体相同,可以强制转换成 sockaddr。

1
2
3
4
5
6
7
8
9
10
struct sockaddr_in {
short int sin_family; // 协议族 (AF_INET)
unsigned short int sin_port; // 16位端口号 ,大端序。用htons(整数的端口)转换。
struct in_addr sin_addr; // IP地址
unsigned char sin_zero[8]; // 填充,使得结构体大小与sockaddr一致(不用管)
};

struct in_addr {
unsigned long s_addr; // 32位的IP地址,大端序 (使用网络字节序)
};

因此,在实际的网络编程中,先定义 sockaddr_in结构体把相关信息存储之后,再强制转换成 sockaddr,毕竟提供的API接受的类型是 sockaddr。

gethostbyname函数

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <netdb.h>

struct hostent *gethostbyname(const char *name);

struct hostent {
char *h_name; // 主机的正式名称
char **h_aliases; // 主机的别名列表
int h_addrtype; // 地址类型,通常为 AF_INET
int h_length; // 地址长度,通常为 4(对于 IPv4)
char **h_addr_list; //地址列表,可能包含多个 IP 地址(网络字节序)
};

#define h_addr h_addr_list[0] // For backward compatibility.

这个函数的优点就是不仅可以直接传递IP地址(字符串类型或字符数组类型),还支持传递域名。根据返回的hostent结构体中的成员,添加到所需的其它结构体中。

如下是部分应用核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
// 定义主机名
const char* hostname = "www.example.com";

// 获取主机信息
struct hostent* host_info = gethostbyname(hostname);

// 设置服务器地址结构
struct sockaddr_in server_addr;
std::memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(80); // HTTP 端口号
std::memcpy(&server_addr.sin_addr, host_info->h_addr_list[0], host_info->h_length);

字符串IP与大端序IP地址的转换

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

inet_addr:将字符串形式的 IP 地址转换为 in_addr_t

1
2
3
4
5
6
in_addr_t inet_addr(const char *cp);

/*
const char *ip = "127.0.0.1";
in_addr_t addr = inet_addr(ip);
*/

inet_aton:将字符串形式的 IP 地址转换为 in_addr 结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int inet_aton(const char *cp, struct in_addr *inp);

cp: 指向一个以点分十进制表示的IPv4地址的字符串
inp: 指向一个 in_addr 结构体,用于存储转换后的IP地址

/*
const char *ip = "127.0.0.1";
struct in_addr addr;

if (inet_aton(ip, &addr) == 0) {
std::cerr << "Invalid IP address" << std::endl;
} else {
std::cout << "inet_aton: " << addr.s_addr << std::endl;
}
*/

inet_ntoa:将 in_addr 结构体中的 IP 地址转换为字符串形式

1
2
3
4
5
6
7
8
9
10
11
12
13
char *inet_ntoa(struct in_addr in);

/*
struct in_addr addr;
addr.s_addr = inet_addr("127.0.0.1");

char *ip = inet_ntoa(addr);
if (ip == nullptr) {
std::cerr << "Error converting address" << std::endl;
} else {
std::cout << "inet_ntoa: " << ip << std::endl;
}
*/

注:typedef unsigned int in_addr_t 代表32位大端序的IP地址。