字节序(byte order)关系到多字节整数(short/int16、int/int32,int64)和浮点数的各字节在内存中的存放顺序。字节序分为两种:小端字节序(little endian)和大端字节序(big endian)。小端字节序:低字节存放在内存低地址,例如对两字节整数0x0100(十进制数256),低字节00放在低地址(假设地址为0x0041f880),高字节01放在高地址0x0041f881。大端字节序:高字节在低地址,同样是0x0100,高字节01放在低地址(假设地址为0x0041f880),低字节00放高地址0x0041f881。可见对相同的两字节整数,在不同字节序的机器上其内存布局是不同的,反过来内存布局相同的,在不同字节序的机器上被解释为不同的整数值,除非这几个字节值相同。
字节序是由cpu处理器架构决定的,和操作系统无关,例如Intel cpu采用小端字节序,PowerPC采用大端字节序,有些cpu例如Alpha支持两种字节序,但在使用时要设置具体采用哪一种字节序,不可能同时用两种。主机字节序(host byte order)就是指当前机器的字节序,根据cpu处理器的架构和设置,主机字节序可为小端字节序或大端字节序。关于字节序问题,较全面的描述见https://en.wikipedia.org/wiki/Endianness。
在socket网络编程中通常会涉及到多字节整数、浮点数的传输,如果两台机器字节序不同,直接传多字节整数或浮点数会导致双方将这些多字节解释成不同的数字,所以要在网络协议中规定编解码方式,例如有的协议将整数编码成字符串来避免字节序问题,但只要协议中有多字节整数,都要规定采用什么字节序来表示协议中的多字节整数(除非能保证两台机器的字节序是相同的),也就出现了网络字节序,网络字节序其实就是大端字节序,协议当然也可采用小端字节序,只要双方统一就行。
如上所述,在设计网络二进制协议时,对多字节整数,要规定打包传输时的字节序:网络字节序还是小端字节序。客户端和服务器代码在打包和解包时,对多字节整数,要进行主机字节序和协议规定的字节序的相互转化。
Java应用通常使用java.nio.ByteBuffer进行协议数据的打包和解包,其order(ByteOrder bo)方法可设置打包或解包使用的字节序;如果使用netty框架,可使用ByteBuf类的order方法。
C/C++应用通常使用C库中的如下函数来进行主机字节序和网络字节序的相互转换。
// hton* 主机字节转网络字节序
uint64_t htonll(uint64_t hostlonglong);
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
// ntoh* 网络字节序转主机字节序
uint64_t ntohll(uint64_t hostlonglong);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
linux系统在endian.h头文件中提供了更多的函数进行主机字节和大小端字节序的相互转换,如下:
uint16_t htobe16(uint16_t host_16bits);
uint16_t htole16(uint16_t host_16bits);
uint16_t be16toh(uint16_t big_endian_16bits);
uint16_t le16toh(uint16_t little_endian_16bits);
uint32_t htobe32(uint32_t host_32bits);
uint32_t htole32(uint32_t host_32bits);
uint32_t be32toh(uint32_t big_endian_32bits);
uint32_t le32toh(uint32_t little_endian_32bits);
uint64_t htobe64(uint64_t host_64bits);
uint64_t htole64(uint64_t host_64bits);
uint64_t be64toh(uint64_t big_endian_64bits);
uint64_t le64toh(uint64_t little_endian_64bits);
htobe*(例如htobe16)表示主机字节序到大端字节序(网络字节序);htole*表示主机字节序到小端字节序;be*toh表示大端到主机;le*toh表示小端到主机。
上面的字节序转换函数有个缺点,就是方法太多不方便使用,需要根据多字节整数的类型(uint16_t/int16_t/uint32_t/int32_t/uint64_t/int64_t)来调用不同的转换函数,所以在c++应用中利用模板技术编写了4个统一的字节序转换函数,和整数的类型无关。如下:
/*
* ByteOrderUtil.h
*
* Created on: Nov 15, 20xx
* Author: wanshi
*/
#ifndef BYTEORDERUTIL_H_
#define BYTEORDERUTIL_H_
#include <stdint.h>
namespace ByteOrder {
const uint16_t us_flag = 1;
// little_end_flag 表示主机字节序是否小端字节序
const bool little_end_flag = *((uint8_t*)&us_flag) == 1;
//小端到主机
template<typename T> T le_to_host(T& from)
{
T to;
uint8_t byteLen = sizeof(T);
if(little_end_flag){
return from;
}
else{
char* to_char = (char*)&to;
char* from_char = (char*)&from;
for(int i=0;i<byteLen;i++){
to_char[i] = from_char[byteLen-i-1];
//此处也可用移位操作来实现
}
return to;
}
}
//主机到小端
template<typename T> T host_to_le(T& from)
{
return le_to_host(from);
}
//大端到主机
template<typename T> T be_to_host(T& from)
{
T to;
uint8_t byteLen = sizeof(T);
if(!little_end_flag){
return from;
}
else{
char* to_char = (char*)&to;
char* from_char = (char*)&from;
for(int i=0;i<byteLen;i++){
to_char[i] = from_char[byteLen-i-1];
//此处也可用移位操作来实现
}
return to;
}
}
//主机到大端
template<typename T> T host_to_be(T& from)
{
return be_to_host(from);
}
}
#endif /* BYTEORDERUTIL_H_ */
使用演示:
#include "ByteOrderUtil.h"
using namespace ByteOrder;
int main(int argc,char** argv)
{
uint16_t u16t = 0x1514;
//host到小端
uint16_t leu16t = host_to_le(u16t);
uint16_t hu16t = le_to_host(leu16t);
uint64_t u64t = 0x15141312;
//host到大端
uint64_t beu64t = host_to_be(u64t);
uint64_t hu64t = be_to_host(beu64t);
return 0;
}
相关推荐
在C/C++中,实现GBK到Unicode或Unicode到GBK的转换,主要步骤包括读取编码转换表、解析输入字符串、以及进行逐字节的转换。编码转换表通常是一个二维数组,其中每个元素对应一个GBK编码和对应的Unicode编码。对于GBK...
`Boolean`和`ByteBool`在Delphi中表示1字节的布尔类型,它们可以使用C/C++中的任意1字节类型来表示。 - **WordBool (2字节布尔类型)** `WordBool`是2字节的布尔类型,在C/C++中可以使用任意2字节类型来表示。 - *...
该程序将 BMP/TIFF/JPG/PNG 文件转换为嵌入的 C/C++ 字节数组。 当您想在显示器上显示单色图像时,这非常有用。 它在数据数组生成之前将您的图像转换为单色。 源图像中较暗的颜色将产生“开”像素,而较亮的颜色将...
在IT领域,有时候我们需要利用不同编程语言的优势来完成复杂任务,比如使用PHP处理Web应用程序,同时结合C或C++的高效性能。这篇笔记主要探讨如何在PHP中调用由C或C++编译生成的动态链接库(.so文件),以此实现PHP...
字符型数据在内部以ASCII码形式存储,C/C++中的字符型数据在运算时会被转换为整型数进行处理。 **字符常数表示**: - 直接用单引号括起来的字符,如`'a'`。 - 使用字符的ASCII码值表示,如`\101`代表`'E'`。 字符...
以下是在Linux环境下使用C++实现主机字节序与网络字节序转换的示例代码: ```c++ #include <arpa/inet.h> #include int main() { int port = 6000; int netPort = htonl(port); printf("netPort=%d\n", ...
由于C/C++标准并未规定如何存储这些类型的数据,因此,不同的处理器架构可能会有不同的字节序选择。例如,Intel x86和x64系列处理器使用小端模式,而Motorola 68000和PowerPC系列则采用大端模式。 面试中,能够熟练...
总的来说,C/C++中的CS模式实现涉及到网络编程的基本概念,如套接字、端口、协议等,以及数据的序列化、字节序转换等技术。通过"UDPserver"和"UDPclient"这两个示例程序,我们可以学习到如何在C/C++中使用UDP协议...
例如,C/C++中的字节序可能是大端或小端,而Java默认使用网络字节序(大端)。 实现互通时,还需要考虑密钥管理和同步问题。如果C/C++和Java使用相同的密钥进行加密解密,那么密钥的安全传输和存储就至关重要。一种...
在C或C++中实现Base64编解码可以帮助开发者处理这些场景。本节将深入探讨Base64的概念、工作原理以及如何用C/C++进行实现。 1. **Base64概念** Base64是一种将任意二进制数据转换为可打印ASCII字符的编码方法。它...
- **面向对象编程**:虽然C++是基于C语言开发的,但它引入了面向对象编程的概念,包括类、对象、继承和多态等特性,这些特性使得C++能够支持更复杂的应用程序开发。 - **模板**:C++提供了模板机制,允许开发者编写...
- Base64编码是将每3个字节(24位)的数据转换为4个6位的字符(每个字符对应Base64字符集中的一个字符),最后不足3字节的数据会用0填充,确保编码后的数据长度是4的倍数。 - Base64字符集包含65个字符:大小写...
这些知识点在C/C++面试和笔试中至关重要,它们涵盖了基本类型、内存管理、类型转换和对象初始化等多个方面。理解并熟练应用这些概念是成为一名优秀的C/C++开发者的基础。在面试中,能够清晰地解释这些问题的解决方案...
- 打开Sample1.h,可以看到Java方法签名已经转换成了C/C++函数原型。例如,`JNIEXPORT void JNICALL Java_Sample1_callNative(JNIEnv *, jobject);` - 创建Sample1.cpp文件,实现这个函数。在这里,你可以调用任何...
当Java程序需要与使用Little-Endian的系统(如C/C++)交换数据时,就需要进行字节序转换。例如,上述代码展示了如何实现一个简单的字节序反转函数`ReversEndian()`,用于将Little-Endian数据转换为Big-Endian。在...
这种转换涉及到字节顺序的问题,因为不同的计算机架构(如大端序和小端序)可能以不同的方式存储数据。 大端序(Big-Endian)是指最高有效字节位于最低地址,而最低有效字节位于最高地址。相反,小端序(Little-...
《C/C++中文手册》是为C++和C语言学习者提供的重要参考资料,特别是其中包含了C++17标准,这是C++发展的一个重要里程碑。C++17在之前的C++11和C++14标准基础上,引入了更多优化和新特性,使得编程更加高效、简洁。 ...
在C/C++编程中,文件操作是至关重要的,它允许我们与磁盘上的文件进行交互,包括读取、写入和处理数据。本篇将详细解释如何在VC++环境中使用MFC实现文件的读取与写入,以及可能遇到的问题。 1. **文件的读取**: -...
通过以上详细的步骤和技术要点,我们可以看到,无论是Java调用C/C++还是C/C++调用Java,都需要仔细地处理数据类型转换、异常处理以及性能优化等问题。掌握这些技术细节,对于开发高效稳定的跨语言应用程序至关重要。