大小端及网络字节序分析
一、什么是大端模式、小端模式
- 大端字节序(Big Endian):最高有效位存于最低内存地址处,最低有效位存于最高内存处;
- 小端字节序(Little Endian):最高有效位存于最高内存地址,最低有效位存于最低内存处。
二、Wireshark抓包分析大小端
小端:如下图
即在抓包的时候显示的数据是颠倒顺序显示的,即4字节的数值显示的值是:0x01 0x00 0x00 0x00,但是实际的值是0x00 0x00 0x00 0x01即值为1
大端:如下图
即在抓包的时候显示的数据是正常顺序显示的,即4字节的数值显示的值是:0x00 0x00 0x10 0x00,实际值就是4096
三、网络字节序
网络上传输的数据都是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理,是一个比较有意义的问题;
UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的;
所以说,网络字节序是大端字节序;
在实际中,当在两个存储方式不同的主机上传输时,需要借助字节序转换函数。
四、C#字节数据与数值的转换
/// <summary>
/// byte字节数组转int(大端)
/// </summary>
/// <param name="src">数据源</param>
/// <param name="offset">数据所在的偏移量</param>am>
/// <param name="len">要转换的长度</par
/// <returns></returns>
public static int BytesToInt(byte[] src, int offset, int len)
{
switch (len)
{
case 2:
return (src[offset++] & 0xFF) << 8 |
src[offset++];
case 3:
return (src[offset++] & 0xFF) << 16 |
(src[offset++] & 0xFF) << 8 |
src[offset++];
case 4:
return (src[offset++] & 0xFF) << 24 |
(src[offset++] & 0xFF) << 16 |
(src[offset++] & 0xFF) << 8 |
src[offset++];
default:
return 0;
}
}
/// <summary>
/// int转byte字节数组(大端)
/// </summary>
/// <param name="value">数值</param>
/// <param name="len">转换的长度</param>
/// <returns></returns>
public static byte[] IntToBytes(int value, int len)
{
byte[] src = new byte[len];
switch (len)
{
case 1:
src[0] = (byte)(value & 0xFF);
break;
case 2:
src[0] = (byte)((value >> 8) & 0xFF);
src[1] = (byte)(value & 0xFF);
break;
case 3:
src[0] = (byte)((value >> 16) & 0xFF);
src[1] = (byte)((value >> 8) & 0xFF);
src[2] = (byte)(value & 0xFF);
break;
case 4:
src[0] = (byte)((value >> 24) & 0xFF);
src[1] = (byte)((value >> 16) & 0xFF);
src[2] = (byte)((value >> 8) & 0xFF);
src[3] = (byte)(value & 0xFF);
break;
}
return src;
}
/// <summary>
/// int转byte字节数组(小端)
/// </summary>
/// <param name="value">数值</param>
/// <param name="len">要转换成字节数组的长度</param>
/// <returns></returns>
public static byte[] IntToBytesEx(int value, int len)
{
byte[] src = new byte[len];
switch (len)
{
case 1:
src[0] = (byte)(value & 0xFF);//低位
break;
case 2:
src[1] = (byte)((value >> 8) & 0xFF);//高8位
src[0] = (byte)(value & 0xFF);//低位
break;
case 3:
src[2] = (byte)((value >> 16) & 0xFF);
src[1] = (byte)((value >> 8) & 0xFF);//高8位
src[0] = (byte)(value & 0xFF);//低位
break;
case 4:
src[3] = (byte)((value >> 24) & 0xFF);
src[2] = (byte)((value >> 16) & 0xFF);
src[1] = (byte)((value >> 8) & 0xFF);//高8位
src[0] = (byte)(value & 0xFF);//低位
break;
}
return src;
}
/// <summary>
/// byte字节数组转int(小端)
/// </summary>
/// <param name="src"></param>
/// <param name="offset"></param>
/// <param name="len"></param>
/// <returns></returns>
public static int BytesToIntEx(byte[] src, int offset, int len)
{
switch (len)
{
case 2:
return ((src[offset++] & 0xFF) | //低位
((src[offset++] & 0xFF) << 8));
case 3:
return ((src[offset++] & 0xFF) | //低位
((src[offset++] & 0xFF) << 8) |
((src[offset++] & 0xFF) << 16));
case 4:
return ((src[offset] & 0xFF) |//低位
((src[offset++] & 0xFF) << 8) |
((src[offset++] & 0xFF) << 16) |
((src[offset++] & 0xFF) << 24));
default:
return 0;
}
}