字节序及IP地址转换

文章目录

字节序

  • 定义:多字节数据在内存中存储或网络传输时各字节的顺序
  • 两种类型:
    • 大端字节序(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';
    }
}

跨平台字节序处理

相关推荐
集大周杰伦3 个月前
Linux网络编程核心实践:TCP/UDP socket与epoll高并发服务器构建
linux·tcp/ip·网络编程·socket·字节序·套接字·i/o多路复用
理论最高的吻8 个月前
HJ33 整数与IP地址间的转换【牛客网】
c++·算法·牛客网·ip地址转换
爱喝酸奶!2 年前
计算机网络:网络字节序
linux·网络·网络字节序·字节序