当前位置: 首页 > news >正文

【linux网络编程】字节序

在套接字(Socket)通信中,字节序(Endianness)是指多字节数据在内存或网络传输中的存储顺序。主要有两种字节序:

  1. 大端字节序(Big-Endian):最高有效字节(MSB,Most Significant Byte)存储在低地址,最低有效字节(LSB,Least Significant Byte)存储在高地址。例如:

    int num = 0x12345678;
    

    在大端模式下,内存存储顺序为:

    0x12 0x34 0x56 0x78
    
  2. 小端字节序(Little-Endian):最低有效字节(LSB)存储在低地址,最高有效字节(MSB)存储在高地址。例如:

    0x78 0x56 0x34 0x12
    

套接字通信中的字节序

网络通信通常涉及不同架构的计算机,而不同架构可能使用不同的字节序。为了保证数据的可移植性,网络协议统一采用大端字节序(即网络字节序,Network Byte Order)。这意味着,在发送数据时,需要将数据转换为大端格式;在接收数据时,需要从大端格式转换回主机字节序。

字节序转换函数

在C/C++的Socket编程中,提供了一组字节序转换函数,用于在主机字节序和网络字节序之间进行转换:

  • 主机字节序 → 网络字节序

    • htons():转换16位无符号短整数(short)
    • htonl():转换32位无符号整数(long)
  • 网络字节序 → 主机字节序

    • ntohs():转换16位无符号短整数(short)
    • ntohl():转换32位无符号整数(long)

示例代码

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>

int main() {
    uint16_t host_short = 0x1234;
    uint32_t host_long = 0x12345678;

    uint16_t net_short = htons(host_short);
    uint32_t net_long = htonl(host_long);

    printf("Host short: 0x%x, Network short: 0x%x\n", host_short, net_short);
    printf("Host long: 0x%x, Network long: 0x%x\n", host_long, net_long);

    return 0;
}

运行结果(假设主机是小端字节序):

Host short: 0x1234, Network short: 0x3412
Host long: 0x12345678, Network long: 0x78563412

这样,在网络传输时,所有数据都被转换为大端格式,不同架构的计算机都可以正确解析数据。

无论是客户端还是服务器,发送数据时都应该转换为大端(网络字节序)后再发送,接收数据时再转换回主机字节序。这样可以确保不同架构的计算机都能正确解析数据。

发送数据时:

  1. 先检查主机的字节序(小端或大端)。
  2. 使用 htonl()(32位)或 htons()(16位)将数据转换为 大端字节序
  3. 通过 send()sendto() 发送数据。

接收数据时:

  1. 通过 recv()recvfrom() 接收数据。
  2. 使用 ntohl()(32位)或 ntohs()(16位)将数据从 大端字节序转换回主机字节序
  3. 解析和使用数据。

代码示例:

服务器端(接收整数并转换回主机字节序)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    uint32_t net_data, host_data;

    // 创建 socket
    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(PORT);

    // 绑定 socket
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

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

    printf("Waiting for connection...\n");
    new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    if (new_socket < 0) {
        perror("Accept failed");
        exit(EXIT_FAILURE);
    }

    // 接收数据(网络字节序)
    recv(new_socket, &net_data, sizeof(net_data), 0);
    
    // 转换为主机字节序
    host_data = ntohl(net_data);
    printf("Received data (Network Byte Order): 0x%x\n", net_data);
    printf("Converted to Host Byte Order: %u\n", host_data);

    close(new_socket);
    close(server_fd);
    return 0;
}

客户端(发送整数,转换为大端字节序)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    uint32_t host_data = 12345;  // 主机字节序
    uint32_t net_data;

    // 创建 socket
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("Socket creation error");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 连接服务器
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("Invalid address/ Address not supported");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("Connection failed");
        return -1;
    }

    // 转换为网络字节序
    net_data = htonl(host_data);

    // 发送数据
    send(sock, &net_data, sizeof(net_data), 0);
    printf("Sent data (Host Byte Order): %u\n", host_data);
    printf("Converted to Network Byte Order: 0x%x\n", net_data);

    close(sock);
    return 0;
}

运行流程:

  1. 服务器端运行,等待连接:

    Waiting for connection...
    
  2. 客户端连接服务器并发送整数 12345(转换为大端字节序 0x00003039):

    Sent data (Host Byte Order): 12345
    Converted to Network Byte Order: 0x3039
    
  3. 服务器端接收到数据并转换回主机字节序:

    Received data (Network Byte Order): 0x3039
    Converted to Host Byte Order: 12345
    

总结:

  1. 所有数据在发送前都要转换为网络字节序(大端),无论是客户端还是服务器。
  2. 接收数据后,需要转换回主机字节序,保证程序能够正确解析数据。
  3. 使用 htonl()htons() 进行发送转换,ntohl()ntohs() 进行接收转换,确保跨平台兼容性。

相关文章:

  • 第七章 二叉树
  • 生成式AI系列(二) LLM生成质量改善的方法——RAG检索增强生成
  • Python评估网络脆弱性
  • Redis常问八股(一)
  • Java网络编程,多线程,IO流综合项目一一ChatBoxes
  • 初识Qt · 信号与槽 · 自定义和参数
  • 【轻松学C:编程小白的大冒险】---变量的定义、声明与应用场景 06
  • leetcode日记(86)恢复二叉搜索树
  • 2008-2024年中国手机基站数据/中国移动通信基站数据
  • VSTO(C#)Excel开发2:Excel对象模型和基本操作
  • 2025年渗透测试面试题总结-字某跳动-安全研究实习生(三面)(题目+回答)
  • 40岁开始学Java:控制反转IoC
  • 前端知识点---路由模式-实例模式和单例模式(ts)
  • 【redis】全局命令set、get、keys
  • 【linux网络编程】浏览网页时客户端与服务器之间数据交互的完整过程
  • 根据输入汉字生成带拼音的米字格字帖
  • 对接RAGflow的API接口报错
  • Java本地缓存简单实现,支持SpEL表达式及主动、过期、定时删除
  • 解锁日常养生密码,拥抱健康生活
  • RabbitMQ学习笔记
  • 喜欢做木工 网站/东莞做网站seo
  • 自己做网站卖东西/免费发布广告
  • 厦门seo网络优化公司/搜索引擎优化要考虑哪些方面?
  • 专做化妆品的网站/深圳网络推广网站推广
  • 南京建设银行官方网站/软件开发工资一般多少
  • 大连无网站的企业有哪些/站长之家域名查询排行