当前位置:编程学习 > JAVA >>

信息编码:基本整型

 
 
首先,我们来考虑一下简单数据类型,如int,long,char,String等,是如何通过套接字发送和接收的。从前面章节我们已经知道,传输信息时可以通过套接字将字节信息写入一个OutputStream实例中(该实例已经与一个Socket相关联),或将其封装进一个DatagramPacket实例中(该实例将由DatagramSocket发送)。然而,这些操作所能处理的唯一数据类型是字节和字节数组。作为一种强类型语言,Java需要把其他数据类型(int,String等)显式转换成字节数组。所幸的是Java的内置工具能够帮助我们完成这些转换。在第2前面的TCPEchoClient.java示例程序中,我们看到过String类的getBytes()方法,该方法就是将一个Sring实例中的字符转换成字节的标准方式。在考虑数据类型转换的细节之前,我们先来看看大部分基本数据类型的表示方法。
 
 
 
 基本整型
 
 如我们所见,TCP和UDP套接字使我们能发送和接收字节序列(数组),即范围在0-255之间的整数。使用这个功能,我们可以对值更大的基本整型数据进行编码,不过发送者和接收者必须先在一些方面达成共识。一是要传输的每个整数的字节大小(size)。例如,Java程序中,int数据类型由32位表示,因此,我们可以使用4个字节来传输任意的int型变量或常量;short数据类型由16位表示,传输short类型的数据只需要两个字节;同理,传输64位的long类型数据则需要8个字节。
 
下面我们考虑如何对一个包含了4个整数的序列进行编码:一个byte型,一个short型,一个int型,以及一个long型,按照这个顺序从发送者传输到接收者。我们总共需要15个字节:第一个字节存放byte型数据,接下来两个字节存放short型数据,再后面4个字节存放int型数据,最后8个字节存放long型数据。
 
 
我们已经做好深入研究的准备了吗?未必。对于需要超过一个字节来表示的数据类型,我们必须知道这些字节的发送顺序。显然有两种选择:从整数的右边开始,由低位到高位地发送,即little-endian顺序;或从左边开始,由高位到低位发送,即big-endian顺序。(注意,幸运的是字节中位的顺序在实现时是以标准的方式处理的)考虑长整型数123456787654321L,其64位(以十六进制形式)表示为0x0000704885F926B1。如果我们以big-endian顺序来传输这个整数,其字节的十进制数值序列就如下所示:
 
order of transmission:传输顺序
 
 如果我们以little-endian顺序传输,则字节的十进制数组序列为:
 
 order of transmission:传输顺序
 
 关键的一点是,对于任何多字节的整数,发送者和接收者必须在使用big-endian顺序还是使用little-endian顺序上达成共识[ ]。如果发送者使用了little-endian顺序来发送上述整数,而接收者以big-endian顺序对其进行接收,那么接收者将取到错误的值,它会将这个8字节序列的整数解析成12765164544669515776L。
 
发送者和接收者需要达成共识的最后一个细节是:所传输的数值是有符号的(signed)还是无符号的(unsigned)。Java中的四种基本整型都是有符号的,它们的值以二进制补码(two's-complement)的方式存储,这是有符号数值的常用表示方式。在处理有k位的有符号数时,用二进制补码的形式表示负整数-n(1 ≤ n ≤ 2k?1),则补码的二进制值就为2k?n。而对于非负整数p(0 ≤ p ≤ 2k?1 - 1),只是简单地用k位二进制数来表示p的值。因此,对于给定的k位,我们可以通过二进制补码来表示?2k?1到2k?1?1范围的值。注意,最高位(msb)标识了该数是正数(msb = 0)还是负数(msb = 1)。另外,如果使用无符号(unsigned)编码,k位可以直接表示0到2k - 1之间的数值。例如,32位数值0xffffffff(所有位全为1),将其解析为有符号数时,二进制补码整数表示-1;将其解析为无符号数时,它表示4294967295。由于Java并不支持无符号整型,如果要在Java中编码和解码无符号数,则需要做一点额外的工作。在此假设我们处理的都是有符号整数数据。
 
那么我们怎样才能将消息的正确值存入字节数组呢?为了清楚地展示需要做的步骤,我们将对如何使用"位操作(bit-diddling)"(移位和屏蔽)来显式编码进行介绍。示例程序BruteForceCoding.java中有一个特殊的方法encodeIntBigEndian()能够对任何值的基本类型数据进行编码。它的参数包括用来存放数值的字节数组,要进行编码的数值(表示为long型,它是最长的整型,能够保存其他整型的值),数值在字节数组中开始位置的偏移量,以及该数值写到数组中的字节数。如果我们在发送端进行了编码,那么必须能够在接收端进行解码。BruteForceCoding类同时还提供了decodeIntBigEndian()方法,用来将字节数组的子集解码到一个Java的long型整数中。
 
 
 
BruteForceCoding.java
 
0 public class BruteForceCoding {
 
1 private static byte byteVal = 101; // one hundred and
 
one
 
2 private static short shortVal = 10001; // ten thousand
 
and one
 
3 private static int intVal = 100000001; // one hundred
 
million and one
 
4 private static long longVal = 1000000000001L;// one
 
trillion and one
 
5
 
6 private final static int BSIZE = Byte.SIZE / Byte.SIZE;
 
7 private final static int SSIZE = Short.SIZE / Byte.SIZE;
 
8 private final static int ISIZE = Integer.SIZE /
 
Byte.SIZE;
 
9 private final static int LSIZE = Long.SIZE / Byte.SIZE;
 
10
 
11 private final static int BYTEMASK = 0xFF; // 8 bits
 
12
 
13 public static String byteArrayToDecimalString(byte[]
 
bArray) {
 
14 StringBuilder rtn = new StringBuilder();
 
15 for (byte b : bArray) {
 
16 rtn.append(b & BYTEMASK).append(" ");
 
17 }
 
18 return rtn.toString();
 
19 }
 
20
 
21 // Warning: Untested preconditions (e.g., 0 <= size <=
 
8)
 
22 public static int encodeIntBigEndian(byte[] dst, 
 
long val, int offset, int size) {
 
23 for (int i = 0; i < size; i++) {
 
24 dst[offset++] = (byte) (val >> ((size - i - 1) *
 
Byte.SIZE));
 
25 }
 
26 return offset;
 
27 }
 
28
 
29 // Warning: Untested preconditions (e.g., 0 <= size <=
 
8)
 
30 public static long decodeIntBigEndian(byte[] 
 
val, int offset, int size) {
 
31 long rtn = 0;
 
32 for (int i = 0; i < size; i++) {
 
33 rtn = (rtn << Byte.SIZE) | ((long) val[offset + i] &
 
BYTEMASK);
 
34 }
 
35 return rtn;
 
36 }
 
37
 
38 public static void main(String[] args) {
 
39 byte[] message = new byte[BSIZE + SSIZE + ISIZE + LSIZE];
 
40 // Encode the fields in the target byte array
 
41 int offset = encodeIntBigEndian(message, byteVal, 0,
 
BSIZE);
 
42 offset = encodeIntBigEndian(message, shortVal, offset,
 
SSIZE);
 
43 offset = encodeIntBigEndian(message, intVal, offset,
 
ISIZE);
 
44 encodeIntBigEndian(message, longVal, offset, LSIZE);
 
45 System.out.println("Encoded message: " + 
 
byteArrayToDecimalString(messag
补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,