您的位置:首页 > 理论基础 > 计算机网络

两个例子理解主机字节序和网络字节序,不同平台字节序转换

2017-09-10 18:53 513 查看
不同平台存储多字节整形数据的内存顺序不同,分为主机字节序和网络字节序分别对应小端字节序和大端字节序

Java平台存储多字节整形时是大端字节序,如存储int i=0x01020304 在内存中的顺序是 01 02 03 04。先占位高内存,后占位低内存。即数值的高位存储在低内存上

c++ windows平台下是低端字节序 int i=0x01020304 在内存中的顺序是 04 03 02 01。先占位低内存后占位高内存,即数值的高位存储在高内存上

网络和JVM都采用的是大字节序,这种字节序符合人类的习惯。JVM会根据底层的操作系统和CPU自动进行字节序的转换

int i= 0x01020304在Java平台上存储顺序是01 02 03 04,发送后c+ +平台接收到的顺序是01 02 03 04 放到内存中的顺序也是 01 02 03 04,但是在c+ +平台识别 01 02 03 04 代表的int时,是以按照 int j = 0x04 03 02 01 按这个顺序拼好的数将这四字节内存识别为int类型。

举两个例子来理解一下

定义一个int值10000,分别查看在不同平台下的内存排序

//java平台下:
public static void main(String args[]) {
private static final char[] HEX_CHAR = {'0','1','2','3','4','5','6','7','8','9','a','b', 'c', 'd', 'e', 'f'};
int x = 10000;
//用ByteBuffer类将int转为byte数组,这种情况下是按照实际内存中的字节顺序输出,也就是大端字节序
//bb.order(ByteOrder.LITTLE_ENDIAN);此方法可以将输出的数组由大端字节序转为小端字节序排列
ByteBuffer bb = ByteBuffer.wrap(new byte[4]);
bb.asIntBuffer().put(x);
String hex = bytesToHexFun1(bb.array());
System.out.println(hex);
}

//byte数组转十六进制字符串
public static String bytesToHex(byte[] bytes) {
char[] buf = new char[bytes.length * 2];
int a = 0;
int index = 0;
for (byte b : bytes) {
if (b < 0) {
a = 256 + b;
} else {
a = b;
}

buf[index++] = HEX_CHAR[a / 16];
buf[index++] = HEX_CHAR[a % 16];
}

return new String(buf);
}


输出为:00002710

//c++平台下
int i = 10000;
char c[10];
memcpy(c,&i,sizeof(i));


查看内存,排序为10270000

定义一个byte数组 {0x01,0x00,0x00,0x00},分别查看不同平台下转为int时的值

//Java平台下:
byte [] b = {0x01,0x00,0x00,0x00};
int i = byteArrayToInt(b);
System.out.println(i+"");
//是以00000001 00000000 00000000 00000000 输出的i为16777216


//c++平台下:
char c[4] ={0x01,0x00,0x00,0x00};
int* p = (int*)c;//强转
int i=*p;
//是以00000000 00000000 00000000 00000001 输出的i为1


工具方法:

//利用ByteBuffer查看字节序
int x = 10000;
ByteBuffer bb = ByteBuffer.wrap(new byte[4]);
bb.asIntBuffer().put(x);
String ss_before = Arrays.toString(bb.array());
System.out.println("默认字节序 " + bb.order().toString() + "," + " 内存数据 "+ ss_before);

bb.order(ByteOrder.LITTLE_ENDIAN);
bb.asIntBuffer().put(x);
String ss_after = Arrays.toString(bb.array());
System.out.println("修改字节序 " + bb.order().toString() + "," + " 内存数据 "+ ss_after);


int转byte数组

//将int转为按低端字节序排列的byte数组(c++内存存放顺序)
public static byte[] int2ByteArray(int res) {
byte[] targets = new byte[4];
targets[0] = (byte) (res & 0xff);
targets[1] = (byte) ((res >> 8) & 0xff);
targets[2] = (byte) ((res >> 16) & 0xff);
targets[3] = (byte) (res >>> 24);
return targets;
}


//将int转为高端字节序排列的byte数组(Java内存存放顺序)
public static byte[] int2ByteArray(int n) {
byte[] byteArray = null;
try {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream(byteOut);
dataOut.writeInt(n);
byteArray = byteOut.toByteArray();
Arrays.toString(byteArray);
} catch (IOException e) {
e.printStackTrace();
}
return byteArray;
}
//Java API 函数
public final void writeInt(int v) throws IOException {
out.write((v >>> 24) & 0xFF);
out.write((v >>> 16) & 0xFF);
out.write((v >>>  8) & 0xFF);
out.write((v >>>  0) & 0xFF);
incCount(4);
}


byte数组转int

//按低端字节序转int
public static int byteArray2Int(byte[] res) {
int targets = (res[0] & 0xff) | ((res[1] << 8) & 0xff00)| ((res[2] << 24) >>> 8) | (res[3] << 24);
return targets;
}


//按高端字节序转int
public static int byteArrayToInt(byte[] byteArray) {
int n = 0;
try {
ByteArrayInputStream byteInput = new ByteArrayInputStream(byteArray);
DataInputStream dataInput = new DataInputStream(byteInput);
n = dataInput.readInt();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return n;
}
//Java API 函数
public final int readInt() throws IOException {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new EOFException();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}


API使用陷阱:

用OutputStream 的write(int)方法时要注意:

将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。

要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。

比如 传入int 1000,内存占位状态[0, 0, 39, 16],1000的八个低位即为16
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐