有关“字节序”的描述

BIG-ENDIAN(大字节序、高字节序)
LITTLE-ENDIAN(小字节序、低字节序)
主机字节序
网络字节顺序
JAVA字节序

1.BIG-ENDIAN、LITTLE-ENDIAN跟多字节类型的数据有关的比如int,short,long型,而对单字节数据byte却没有影响。BIG-ENDIAN就是低位字节排放在内存的低端,高位字节排放在内存的高端。而LITTLE-ENDIAN正好相反。 
比如 int a = 0x05060708 
在BIG-ENDIAN的情况下存放为: 
字节号 0 1 2 3 
数据 05 06 07 08 
在LITTLE-ENDIAN的情况下存放为: 
字节号 0 1 2 3 
数据 08 07 06 05 

2.BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN、。IA架构的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器。这其实就是所谓的主机字节序。而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序是BIG-ENDIAN。所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。 

3.所以在用C/C++写通信程序时,在发送数据前务必用htonl和htons去把整型和短整型的数据进行从主机字节序到网络字节序的转换,而接收数据后对于整型和短整型数据则必须调用ntohl和ntohs实现从网络字节序到主机字节序的转换。如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是BIG-ENDIAN,只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑字节序的问题了。 

4.如果网络上全部是PowerPC,SPARC和Motorola CPU的主机那么不会出现任何问题,但由于实际存在大量的IA架构的CPU,所以经常出现数据传输错误。 

5.文章开头所提出的问题,就是因为程序运行在X86架构的PC SERVER上,发送数据的一端用C实现的,接收一端是用JAVA实现的,而发送端在发送数据前未进行从主机字节序到网络字节序的转换,这样接收端接收到的是LITTLE-ENDIAN的数据,数据解释自然出错。 
具体数据如下,实际发送的数据为23578 
发送端发送数据: 1A 5C 
接收端接收到数据后,按BIG-ENDIAN进行解释具体数据是多少?你们自己去计算并比较吧!


===============================================================================================

Big Endian and Little Endian 

    谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢? 

    其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB),即常说的低位在先,高位在后。 
    用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示: 

Big Endian 

  低地址                           高地址 
  -----------------------------------------> 
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
  |     12     |      34    |     56      |     78    | 
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

Little Endian 

  低地址                           高地址 
  -----------------------------------------> 
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
  |     78     |      56    |     34      |     12    | 
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

    从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-||| 

    为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?尤其是当你把你在微机上运算的结果运用到计算机群上去的话。在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的 0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。 

    无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了四个转换字节序的宏。


========================================================================================================

/**
* 通信格式转换
*
* Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换
* 高、低字节之间的转换
* windows的字节序为低字节开头
* linux,unix的字节序为高字节开头
* java则无论平台变化,都是高字节开头
*/

public class FormatTransfer {
/**
  * 将int转为低字节在前,高字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toLH(int n) {
  byte[] b = new byte[4];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  b[2] = (byte) (n >> 16 & 0xff);
  b[3] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将int转为高字节在前,低字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toHH(int n) {
  byte[] b = new byte[4];
  b[3] = (byte) (n & 0xff);
  b[2] = (byte) (n >> 8 & 0xff);
  b[1] = (byte) (n >> 16 & 0xff);
  b[0] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将short转为低字节在前,高字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toLH(short n) {
  byte[] b = new byte[2];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  return b;
}

/**
  * 将short转为高字节在前,低字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toHH(short n) {
  byte[] b = new byte[2];
  b[1] = (byte) (n & 0xff);
  b[0] = (byte) (n >> 8 & 0xff);
  return b;
}

 

/**
  * 将将int转为高字节在前,低字节在后的byte数组

public static byte[] toHH(int number) {
  int temp = number;
  byte[] b = new byte[4];
  for (int i = b.length - 1; i > -1; i--) {
    b = new Integer(temp & 0xff).byteValue();
    temp = temp >> 8;
  }
  return b;
}

public static byte[] IntToByteArray(int i) {
    byte[] abyte0 = new byte[4];
    abyte0[3] = (byte) (0xff & i);
    abyte0[2] = (byte) ((0xff00 & i) >> 8);
    abyte0[1] = (byte) ((0xff0000 & i) >> 16);
    abyte0[0] = (byte) ((0xff000000 & i) >> 24);
    return abyte0;
}


*/

/**
  * 将float转为低字节在前,高字节在后的byte数组
  */
public static byte[] toLH(float f) {
  return toLH(Float.floatToRawIntBits(f));
}

/**
  * 将float转为高字节在前,低字节在后的byte数组
  */
public static byte[] toHH(float f) {
  return toHH(Float.floatToRawIntBits(f));
}

/**
  * 将String转为byte数组
  */
public static byte[] stringToBytes(String s, int length) {
  while (s.getBytes().length < length) {
    s += " ";
  }
  return s.getBytes();
}


/**
  * 将字节数组转换为String
  * @param b byte[]
  * @return String
  */
public static String bytesToString(byte[] b) {
  StringBuffer result = new StringBuffer("");
  int length = b.length;
  for (int i=0; i<length; i++) {
    result.append((char)(b & 0xff));
  }
  return result.toString();
}

/**
  * 将字符串转换为byte数组
  * @param s String
  * @return byte[]
  */
public static byte[] stringToBytes(String s) {
  return s.getBytes();
}

/**
  * 将高字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int hBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b >= 0) {
    s = s + b;
    } else {
    s = s + 256 + b;
    }
    s = s * 256;
  }
  if (b[3] >= 0) {
    s = s + b[3];
  } else {
    s = s + 256 + b[3];
  }
  return s;
}

/**
  * 将低字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int lBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b[3-i] >= 0) {
    s = s + b[3-i];
    } else {
    s = s + 256 + b[3-i];
    }
    s = s * 256;
  }
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  return s;
}


/**
  * 高字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short hBytesToShort(byte[] b) {
  int s = 0;
  if (b[0] >= 0) {
    s = s + b[0];
    } else {
    s = s + 256 + b[0];
    }
    s = s * 256;
  if (b[1] >= 0) {
    s = s + b[1];
  } else {
    s = s + 256 + b[1];
  }
  short result = (short)s;
  return result;
}

/**
  * 低字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short lBytesToShort(byte[] b) {
  int s = 0;
  if (b[1] >= 0) {
    s = s + b[1];
    } else {
    s = s + 256 + b[1];
    }
    s = s * 256;
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  short result = (short)s;
  return result;
}

/**
  * 高字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float hBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 低字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float lBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 将byte数组中的元素倒序排列
  */
public static byte[] bytesReverseOrder(byte[] b) {
  int length = b.length;
  byte[] result = new byte[length];
  for(int i=0; i<length; i++) {
    result[length-i-1] = b;
  }
  return result;
}

/**
  * 打印byte数组
  */
public static void printBytes(byte[] bb) {
  int length = bb.length;
  for (int i=0; i<length; i++) {
    System.out.print(bb + " ");
  }
  System.out.println("");
}

public static void logBytes(byte[] bb) {
  int length = bb.length;
  String ut = "";
  for (int i=0; i<length; i++) {
    ut = out + bb + " ";
  }

}

/**
  * 将int类型的值转换为字节序颠倒过来对应的int值
  * @param i int
  * @return int
  */
public static int reverseInt(int i) {
  int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
  return result;
}

/**
  * 将short类型的值转换为字节序颠倒过来对应的short值
  * @param s short
  * @return short
  */
public static short reverseShort(short s) {
  short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
  return result;
}

/**
  * 将float类型的值转换为字节序颠倒过来对应的float值
  * @param f float
  * @return float
  */
public static float reverseFloat(float f) {
  float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
  return result;
}

}

<think>好的,我现在需要帮助用户了解网络字节序相关的函数及其用法,用户提到了hton、ntohl等函数,并希望得到定义和用法的解释。首先,我需要回忆之前用户的问题和我的回答,确保信息的一致性。 用户之前询问了struct sockaddr_in的头文件,我详细解释了结构体的定义和使用,并提到了hton等函数。现在用户进一步询问这些函数的详细信息,需要更深入的回答。根据用户提供的引用内容,引用了三个来源,分别解释了字节序转换函数的作用和用法。需要整合这些信息,确保准确性和完整性。 首先,要明确网络字节序是大端模式,而主机字节序可能因系统而异,通常是x86的小端。因此,转换函数如htons、htonl、ntohs、ntohl用于主机和网络字节序之间的转换。引用中提到htons用于将short类型从主机转网络,htonl处理long类型,ntohs和ntohl则是反向转换。此外,inet_addr和inet_pton用于地址转换,但用户这次的问题集中在字节序函数,可能需要简要提及。 接下来,我需要组织回答的结构。先介绍网络字节序和主机字节序的区别,然后逐个函数说明,包括功能、参数、返回值和示例。可能还需要注意事项,比如何时需要转换,以及常见的错误点。根据用户提供的引用,需要注意函数所在的头文件,比如#include <arpa/inet.h>。 另外,用户可能对实际使用场景感兴趣,比如在socket编程中设置端口和地址时需要转换字节序。例如,在之前的示例代码中,使用了htons和inet_addr,可以引用这些例子来说明函数的实际应用。 需要检查是否有混淆的地方,比如htonl和ntohl的区别,确保用户不会误解函数的方向。同时,注意函数命名的规律:h代表host,n代表network,s代表short,l代表long,这样用户可以通过函数名记忆功能。 还要考虑用户可能的后续问题,比如处理不同数据类型时的选择(short vs long),或者在IPv6中的使用情况,但根据用户当前的问题,主要集中在IPv4的相关函数,所以暂时不需要深入IPv6。 最后,确保回答符合之前的格式要求,使用中文,结构清晰,分点说明,代码示例正确,并附上相关问题。同时,引用之前提供的引用标记,如[^1][^2][^3],但需要确认是否正确对应。</think>### 网络字节序转换函数详解 在 **网络编程** 中,由于不同计算机可能使用不同的字节序(大端/小端),必须通过标准函数统一数据格式为 **网络字节序(大端)**。以下是关键函数及其用法: --- #### 1. 核心函数与定义 所有函数需包含头文件: ```c #include <arpa/inet.h> // 主要定义字节序转换和地址转换函数 ``` | 函数名 | 功能描述 | 参数类型 | 返回值类型 | |-----------|------------------------------------------------------------|--------------|-------------| | `htons()` | 将16位短整型(`uint16_t`)从主机字节序转换为网络字节序 | `uint16_t` | `uint16_t` | | `htonl()` | 将32位长整型(`uint32_t`)从主机字节序转换为网络字节序 | `uint32_t` | `uint32_t` | | `ntohs()` | 将16位短整型(`uint16_t`)从网络字节序转换回主机字节序 | `uint16_t` | `uint16_t` | | `ntohl()` | 将32位长整型(`uint32_t`)从网络字节序转换回主机字节序 | `uint32_t` | `uint32_t` | --- #### 2. 函数命名规则 - **`h`**:主机字节序(Host) - **`n`**:网络字节序(Network) - **`s`**:短整型(Short,16位,如端口号) - **`l`**:长整型(Long,32位,如IPv4地址) --- #### 3. 使用场景与示例 ##### (1) 设置端口号(`htons()`) ```c uint16_t port = 8080; uint16_t net_port = htons(port); // 主机字节序网络字节序 printf("转换后端口号:%d → 0x%x\n", port, net_port); ``` 输出示例: ```text 转换后端口号:8080 → 0x901f ``` ##### (2) 解析接收数据(`ntohl()`) ```c uint32_t net_ip = 0x7f000001; // 网络字节序的IP(0x7f.0x00.0x00.0x01 → 127.0.0.1) uint32_t host_ip = ntohl(net_ip); // 网络字节序 → 主机字节序 printf("转换后IP:%u → %u\n", net_ip, host_ip); ``` 输出示例(小端机器): ```text 转换后IP:2130706433 → 16777343 ``` --- #### 4. 注意事项 1. **必须转换的数据类型** - **端口号**:使用 `htons()`(16位)或 `htonl()`(32位) - **IP地址**:使用 `inet_addr()` 或 `inet_pton()`(自动处理字节序)[^1] - **自定义协议字段**:根据数据类型选择对应函数转换 2. **常见错误** - 混淆 `htons()` 和 `htonl()`(如将32位IP用 `htons()` 转换) - 未转换直接发送数据(导致跨平台通信错误)[^3] --- #### 5. 相关地址转换函数 | 函数名 | 功能描述 | 示例 | |----------------|---------------------------------|-------------------------------| | `inet_addr()` | 将点分十进制IP转换为32位网络字节序 | `inet_addr("192.168.1.1")` | | `inet_ntoa()` | 将32位网络字节序IP转换为字符串 | `inet_ntoa(struct in_addr)` | | `inet_pton()` | 跨协议版本(IPv4/IPv6)的地址转换 | `inet_pton(AF_INET, "::1", &addr)` | --- ### 相关问题 1. 如何检测当前系统的字节序是大端还是小端? 2. 为什么IPv6编程中需要使用 `inet_pton()` 而不是 `inet_addr()`? 3. 如果发送一个 `int` 类型数据(非标准协议),应该如何保证接收端正确解析? --- ### 参考引用 [^1]: 网络字节序与主机字节序转换函数解析 [^2]: `htonl` 等函数的功能及头文件定义 [^3]: 字节序转换在套接字编程中的必要性
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值