C++网络编程之字节序

概述

字节序是指多字节数据在内存中的存储顺序。主要有两种字节序:大端序、小端序。

大端序:即Big-Endian,高位字节存放在低地址处,低位字节存放在高地址处。比如:16位整数0x1234,在大端序下会以0x12 0x34的形式存储。

小端序:即Little-Endian,低位字节存放在低地址处,高位字节存放在高地址处。比如:16位整数0x1234,在小端序下会以0x34 0x12的形式存储。

需要注意的是:不同计算机系统可能使用不同的字节序来存储多字节的数据类型。当这些系统之间进行通信时,如果不正确处理字节序问题,就可能会导致数据解析错误。比如:如果一台机器是小端序,另一台是大端序,它们之间的数据交换如果没有经过适当的转换,接收方可能会误解发送方的数据。

检测字节序

在C++中,检测字节序有几种不同的方法。下面,分别进行介绍。

1、使用类型转换。下面的代码封装了一个IsLittleEndian的函数,用于判断是否为小端序。在该函数中,首先初始化一个32位无符号整数uiValue,其值为0x01020304。然后,使用reinterpret_cast将uiValue的地址转换为char*类型的指针,以便逐字节访问。检查uiValue的第一个字节是否为0x04,如果是,则系统是小端序;否则,系统是大端序。

C++ 复制代码
#include <iostream>
using namespace std;

bool IsLittleEndian()
{
    unsigned int uiValue = 0x01020304;
    char* pValue = reinterpret_cast<char*>(&uiValue);

    // 如果第一个字节是0x04,则是小端序
    return (pValue[0] == 0x04);
}

int main()
{
    if (IsLittleEndian())
    {
        cout << "Little-Endian" << endl;
    }
    else
    {
        cout << "Big-Endian" << endl;
    }

    return 0;
}

2、使用联合体。联合体允许我们在相同的内存位置存储不同的数据类型,这样可以方便地检查多字节数据类型的字节顺序。在下面的代码中,我们首先使用联合体将一个32位无符号整数unsigned int i和一个4个字符的数组char c[4]共享同一块内存。然后,初始化联合体中的unsigned int i为0x01020304。最后,通过char c[4]访问unsigned int i的第一个字节。检查第一个字节是否为0x04,如果是,则系统是小端序;否则,系统是大端序。

C++ 复制代码
#include <iostream>
using namespace std;

bool IsLittleEndian()
{
    union {
        unsigned int i;
        char c[4];
    } value = {0x01020304};

    // 如果第一个字节是0x04,则是小端序
    return (value.c[0] == 0x04);
}

int main()
{
    if (IsLittleEndian())
    
    {
        cout << "Little-Endian" << endl;
    }
    else
    {
        cout << "Big-Endian" << endl;
    }

    return 0;
}

3、使用标准库函数。在某些情况下,可以使用标准库提供的函数来检测字节序。比如:在POSIX兼容的系统中,可以使用endian.h头文件中的宏来检测字节序。具体如何使用,可参考下面的示例代码。

C++ 复制代码
#include <iostream>
#include <endian.h>
using namespace std;

int main()
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    cout << "Little-Endian" << endl;
#elif __BYTE_ORDER == __BIG_ENDIAN
    cout << "Big-Endian" << endl;
#else
    cout << "Unknown endianness" << endl;
#endif

    return 0;
}

主机字节序与网络字节序

主机字节序是指当前计算机系统中,多字节数据类型的存储顺序。主要有上面介绍的两种字节序:大端序和小端序。

在网络协议中,为了确保数据的一致性,通常规定使用一种统一的字节序。最常见的网络字节序是大端序,这是因为:大端序与人类阅读数字的习惯一致,便于调试和理解。比如:在TCP/IP协议族中,所有多字节的数据类型都要求使用大端序进行传输。

字节序转换

在跨平台或异构网络环境中,不同的系统可能使用不同的字节序。如果发送方和接收方的字节序不一致且没有进行适当的转换,会导致数据解析错误。因此,在网络编程中,必须确保数据在发送前转换为网络字节序,在接收后转换回主机字节序。

C++标准库提供了几个用于字节序转换的函数。对于POSIX兼容系统(比如:Linux、Mac等),这些函数定义在arpa/inet.h头文件中。对于Windows系统,这些函数定义在winsock2.h头文件中。

htons()和htonl():用于将主机字节序转网络字节序(Host to Network Short/Long)。

ntohs()和ntohl():用于将网络字节序转主机字节序(Network to Host Short/Long)。

在下面的示例代码中,我们使用htons和htonl将主机字节序的hostShort、hostLong转换成了网络字节序的netShort、netLong。然后,再将网络字节序的netShort、netLong转换回主机字节序的convertedShort、convertedLong。注意:在Windows下使用Visual Studio编译这段程序时,需要在工程中链接ws2_32.lib库,否则链接时会报错。

C++ 复制代码
#include <iostream>
#include <winsock2.h>
using namespace std;

int main()
{
    // 主机字节序转为网络字节序
    uint16_t hostShort = 0x1234;
    uint32_t hostLong = 0x12345678;

    uint16_t netShort = htons(hostShort);
    uint32_t netLong = htonl(hostLong);

    cout << "Host short: " << hex << hostShort << endl;
    cout << "Network short: " << hex << netShort << endl;

    cout << "Host long: " << hex << hostLong << endl;
    cout << "Network long: " << hex << netLong << endl;

    // 再将网络字节序转回主机字节序
    uint16_t convertedShort = ntohs(netShort);
    uint32_t convertedLong = ntohl(netLong);

    cout << "Converted short: " << hex << convertedShort << endl;
    cout << "Converted long: " << hex << convertedLong << endl;

    return 0;
}
相关推荐
jyan_敬言19 分钟前
【C++】入门基础(二)引用、const引用、内联函数inline、nullptr
c语言·开发语言·数据结构·c++·青少年编程·编辑器
UpUpUp……19 分钟前
模拟String基本函数/深浅拷贝/柔性数组
开发语言·c++·算法
清水加冰1 小时前
【Linux网络】HTTPS
网络·网络协议·https
酷熊代理2 小时前
软路由如何屏蔽国外IP?RouterOS保姆级实战教程(附自动化脚本)
运维·网络协议·tcp/ip·自动化·智能路由器·软路由·小猫pptp
攻城狮7号2 小时前
【第九节】windows sdk编程:通用控件的使用
c++·windows·windows编程·windows sdk
曦月逸霜2 小时前
第十次CCF-CSP认证(含C++源码)
数据结构·c++·算法·ccf-csp
眠りたいです3 小时前
Linux:利用System V系列的-共享内存,消息队列实现进程间通信
linux·运维·服务器·c++·进程间通信
moz与京3 小时前
【附JS、Python、C++题解】Leetcode面试150题(9)——三数之和
javascript·c++·leetcode
nqqcat~3 小时前
函数的引用/函数的默认参数/函数的占位参数/函数重载
开发语言·c++·算法
daily_23333 小时前
c++领域展开第十六幕——STL(vector容器的了解以及各种函数的使用)超详细!!!!
开发语言·c++·vector·visual studio code