首页 > 编程笔记 > C++笔记 阅读:7

C++字节序转换的2种方法(附带实例)

在进行跨平台数据交换时,由于不同平台可能采用不同的字节序,因此正确处理字节序转换是确保数据正确传输和解析的关键。

在进行跨平台数据交换时,必须确保发送和接收双方都能正确解析数据。这通常涉及以下两点:

C++字节序转换函数

常用的字节序转换函数有:
  1. htons(host to network short):将 16 位短整数从主机字节序转换为网络字节序;
  2. htonl(host to network long):将 32 位长整数从主机字节序转换为网络字节序;
  3. ntohs(network to host short):将 16 位短整数从网络字节序转换为主机字节序;
  4. ntohl(network to host long):将 32 位长整数从网络字节序转换为主机字节序。

这些函数可以在大多数系统中使用,并且通常包含在 arpa/inet.h(POSIX 系统)或 winsock2.h(Windows 系统)头文件中。

以下示例演示如何在 Linux 环境中处理字节序问题,以确保数据被正确传输和解析。
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

// 检查系统是不是大端字节序
bool is_big_endian() {
    uint16_t num = 0x1;
    return *(reinterpret_cast<char*>(&num)) == 0;
}

// 字节序转换函数
uint16_t swap_endian(uint16_t val) {
    return (val << 8) | (val >> 8);
}

uint32_t swap_endian(uint32_t val) {
    return ((val << 24) & 0xFF0000000) |
    ((val << 8) & 0x00FF0000) |
    ((val >> 8) & 0x0000FF00) |
    ((val >> 24) & 0x000000000FF);
}

void server() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    uint32_t data;

    // 创建服务器套接字
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 绑定服务器地址
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(8080); // 端口号转换为网络字节序
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 接收客户端连接
    new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    if (new_socket < 0) {
        perror("accept");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 接收数据
    read(new_socket, &data, sizeof(data));
    data = ntohl(data); // 转换为主机字节序

    std::cout << "接收到的数据(主机字节序): 0x" << std::hex << data << std::endl;

    close(new_socket);
    close(server_fd);
}

void client() {
    struct sockaddr_in address;
    int sock = 0;
    uint32_t data = 0x12345678;
    struct sockaddr_in serv_addr;

    // 创建客户端套接字
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        std::cerr << "Socket creation error" << std::endl;
        return;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(8080); // 端口号转换为网络字节序

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        std::cerr << "Connection Failed" << std::endl;
        close(sock);
        return;
    }

    data = htonl(data); // 转换为网络字节序
    send(sock, &data, sizeof(data), 0);
    std::cout << "数据已发送" << std::endl;

    close(sock);
}

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        sleep(1); // 等待服务器启动
        client(); // 等待服务器启动
    } else if (pid > 0) {
        server();
    } else {
        std::cerr << "Fork failed" << std::endl;
        return 1;
    }

    return 0;
}
在这个示例中,服务器和客户端都在发送和接收数据时进行了字节序转换。服务器将接收到的网络字节序数据转换为主机字节序进行处理,客户端在发送数据前将主机字节序转换为网络字节序。

C++ std::byteswap字节序转换

在 C++23 中引入的 std::byteswap 为字节序转换提供了一种新的、标准化的解决方案。与传统的网络字节序转换函数(如 htonl() 和ntohl())相比,这个新方案具有以下几个优势:
在跨平台数据交换中,数据通常需要在不同的系统间传输,这些系统可能使用不同的字节序(大端或小端)。在这种情况下,使用 std::byteswap() 可以确保数据在被发送前转换为适当的字节序,以及在被接收后转换回适用于本地处理的字节序。例如:
例如,假设有一个跨平台的应用场景,其中一个小端系统需要发送一个整数给一个大端系统:
#include <bit>
#include <iostream>
#include <cstdint>
int main() {
    uint32_t num = 0x12345678;  // 假设这是需要发送的数据
    std::cout << "原始数据: 0x" << std::hex << num << std::endl;
    // 转换字节序以适应大端接收
    uint32_t swapped = std::byteswap(num);
    std::cout << "转换后的数据: 0x" << std::hex << swapped << std::endl;
    // 假设这是在接收方
    uint32_t received = std::byteswap(swapped);
    std::cout << "接收后的数据(转回原始字节序): 0x" << std::hex << received << std::endl;
    return 0;
}
在这个例子中,std::byteswap() 被用来在发送前将数据的字节序从小端转换为大端,以及在接收到数据后将其转换回小端。

在使用 std::byteswap() 时,最好的做法是:
通过这种方式,std::byteswap() 不仅增强了代码的可移植性和健壮性,还简化了跨平台开发中经常遇到的字节序问题的处理。

相关文章