文章目录
字节序
- 定义:多字节数据在内存中存储或网络传输时各字节的顺序
- 两种类型:
- 大端字节序(Big-endian):高位字节在前(网络标准)
- 小端字节序(Little-endian):低位字节在前(x86/ARM常见)
latex
数值: 0x11223344 (十进制: 287,454,020)
内存地址: 低地址 → → → 高地址
大端存储: [0x11] [0x22] [0x33] [0x44]
小端存储: [0x44] [0x33] [0x22] [0x11]
- 主机字节序:一般主机当中使用小端字节序
- 网络字节序:网络通信当中必须使用大端字节序
检测主机字节序
c
#include <stdint.h>
#include <stdio.h>
void check_endianness() {
uint32_t val32 = 0x11223344;
uint8_t val8 = *((uint8_t*)&val32); // 获取变量val32第一个字节的值
// 通过判断val8的值是0x44或者0x11来确定主机字节序是大端还是小端
if (val8 == 0x44) {
printf("本机是小端字节序\n");
} else if (val8 == 0x11) {
printf("本机是大端字节序\n");
} else {
printf("无法确定字节序\n");
}
}
// ARM架构通常是:小端字节序
// 网络传输必须是:大端字节序(网络字节序)
字节序转换函数

函数原型
c
#include <arpa/inet.h> // 主要头文件
#include <netinet/in.h> // 备用头文件
// ================ 主机到网络(Host to Network)================
// 32位整数转换
uint32_t htonl(uint32_t hostlong);
// 参数:hostlong - 主机字节序的32位整数
// 返回值:网络字节序的32位整数
// 16位整数转换
uint16_t htons(uint16_t hostshort);
// 参数:hostshort - 主机字节序的16位整数
// 返回值:网络字节序的16位整数
// ================ 网络到主机(Network to Host)================
// 32位整数转换
uint32_t ntohl(uint32_t netlong);
// 16位整数转换
uint16_t ntohs(uint16_t netshort);
示例
c
#include <arpa/inet.h>
#include <stdio.h>
int main() {
// 1. 端口号转换(16位)
uint16_t host_port = 8080;
uint16_t net_port = htons(host_port);
printf("主机端口: %d -> 网络端口: %d\n", host_port, net_port);
// 2. IP地址转换(32位)
uint32_t host_ip = 0xC0A80101; // 192.168.1.1
uint32_t net_ip = htonl(host_ip);
printf("主机IP: 0x%08X -> 网络IP: 0x%08X\n", host_ip, net_ip);
return 0;
}
IP地址字符串与二进制转换
- IP地址可能会存在"点分十进制"的字符串形式,转换之前需要注意
- 主机字节序一般采用小端字节序
- 网络字节序转主机字节序以后通常需要转换成"点分十进制"的字符串
传统转换函数(IPv4专用)
c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
void ipv4_conversion_examples() {
struct in_addr addr;
// ===== 方法1: inet_aton (ASCII to Network) =====
// 将点分十进制字符串转换为网络字节序的二进制IP
if (inet_aton("192.168.1.1", &addr) != 0) {
printf("inet_aton: 0x%08X\n", addr.s_addr);
}
// ===== 方法2: inet_addr (已废弃,不推荐使用) =====
// 问题:无法区分错误返回和地址 255.255.255.255
// in_addr_t ip = inet_addr("192.168.1.1");
// ===== 方法3: inet_ntoa (Network to ASCII) =====
// 将网络字节序的二进制IP转换为点分十进制字符串
addr.s_addr = htonl(0xC0A80101);
char* ip_str = inet_ntoa(addr);
printf("inet_ntoa: %s\n", ip_str); // 输出: 192.168.1.1
// 注意:inet_ntoa使用静态缓冲区,非线程安全!
}
现代转换函数(支持IPv4/IPv6)
c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
void modern_ip_conversion_examples() {
// ===== IPv4 示例 =====
struct in_addr ipv4_addr;
char ipv4_str[INET_ADDRSTRLEN]; // IPv4字符串缓冲区大小: 16
// 字符串 -> 二进制 (Presentation to Numeric)
if (inet_pton(AF_INET, "192.168.1.1", &ipv4_addr) == 1) {
printf("IPv4二进制: 0x%08X\n", ipv4_addr.s_addr);
}
// 二进制 -> 字符串 (Numeric to Presentation)
if (inet_ntop(AF_INET, &ipv4_addr, ipv4_str, INET_ADDRSTRLEN) != NULL) {
printf("IPv4字符串: %s\n", ipv4_str);
}
// ===== IPv6 示例 =====
struct in6_addr ipv6_addr;
char ipv6_str[INET6_ADDRSTRLEN]; // IPv6字符串缓冲区大小: 46
if (inet_pton(AF_INET6, "2001:db8::1", &ipv6_addr) == 1) {
printf("IPv6转换成功\n");
}
if (inet_ntop(AF_INET6, &ipv6_addr, ipv6_str, INET6_ADDRSTRLEN) != NULL) {
printf("IPv6字符串: %s\n", ipv6_str);
}
}
线程安全的转换
c
#include <arpa/inet.h>
#include <stdio.h>
// 线程安全的IP地址转换函数
int safe_ip_conversion(const char* ip_str, uint32_t* ip_bin) {
struct in_addr addr;
if (inet_pton(AF_INET, ip_str, &addr) != 1) {
// 转换失败
return -1;
}
*ip_bin = addr.s_addr; // 已经是网络字节序
return 0;
}
void safe_ip_to_string(uint32_t ip_bin, char* buffer, size_t buffer_size) {
struct in_addr addr;
addr.s_addr = ip_bin;
if (inet_ntop(AF_INET, &addr, buffer, buffer_size) == NULL) {
// 转换失败,设置空字符串
buffer[0] = '\0';
}
}
跨平台字节序处理