对于多字节(> 1 字节)类型,如short、int和long等。对于有些cpu体系结构(和操作系统平台无关),如x86,在存储多字节类型的变量数据是,是按照小端的方式存储的,即低位在前,高位在后,如int a = 0x12345678; 其中a的内存地址是0x807D1234,对于小端存储的变量而言,地址0x807D1234存储的字节(byte)为0x78,0x807D1235存储的是0x56,0x807D1236为0x34,0x807D1237为0x12,而端存储的恰恰相反,即高位在前,低位在后。还是针对此例,如果为大端字节序,0x807D1234将存储0x12,0x807D1235存储0x34,0x807D1236存储0x56,0x807D1237存储0x78。
在真实的通讯传输中,由于我们无法预知新加入的服务器或者客户端的主机字节序,因此在设计协议之前便需要确定C/S之间的数据传输编码形式,之后所有的应用程序在进行数据传输之前都需要自行确保发送数据的字节序为协议指定的字节序。
Java的输入输出框架为我们提供了DataInputStream和DataOutputStream两个流对象,与其他流对象不同的是,他们提供了可以直接读取/写入原始数据类型的接口方法,如writeInt、writeBoolean、writeShort、readInt、readLong等。这样我们便可以将这他们封装在SocketInputStream/SocketOutputStream外部,既SocketInputStream/SocketOutputStream分别作为他们的内部流对象用于提供真实的数据流。由于Java中使用的字节序为大端字节序,如果此时系统定义的传输编码方式为小端字节序的话,我们将无法再通过DataInputStream/DataOutputStream直接读取/写入原始数据类型了,为了能够仍然保持这种方便的操作,我们需要提供一对类似的流对象可以将小端字节序中的数据翻转成为Java缺省的大端字节序后,再以ReadInt/writeInt等方法的方式将原始类型返回给调用者或将原始类型重新编码后以小端的方式写出。下面的代码将给出一个小端数据输入输出流对象的实现。
1) 小端数据输出流:
1 public class LEDataOutputStream implements DataOutput {
2 protected final DataOutputStream dout;
3 protected final byte[] work;
4 public LEDataOutputStream(OutputStream out) {
5 dout = new DataOutputStream(out);
6 work = new byte[8];
7 }
8 public final void close() throws IOException {
9 dout.close();
10 }
11 public void flush() throws IOException {
12 dout.flush();
13 }
14 public final int size() {
15 return dout.size();
16 }
17 public final synchronized void write(int ib) throws IOException {
18 dout.write(ib);
19 }
20 public final void write(byte ba[]) throws IOException {
21 dout.write(ba, 0, ba.length);
22 }
23 public final synchronized void write(byte ba[], int off, int len) throws IOException {
24 dout.write(ba, off, len);
26 //boolean由于只是占一个字节,因此可以直接写出了。
27 public final void writeBoolean(boolean v) throws IOException {
28 dout.writeBoolean(v);
29 }
30 //byte和boolean是同样的情况,他们都只是占有一个字节。
31 public final void writeByte(int v) throws IOException {
32 dout.writeByte(v);
33 }
34 //byte的数组也是和byte一样,不需要在翻转了。
35 public final void writeBytes(String s) throws IOException {
36 dout.writeBytes(s);
37 }
38 //由于char是两个字节的,所以这里就需要翻转了。
39 public final void writeChar(int v) throws IOException {
40 work[0] = (byte) v;
41 work[1] = (byte) (v >> 8);
42 dout.write(work, 0, 2);
43 }
44 //对于多字节的数组而言,数据元素在数组中的位置是不变的,需要翻转的是每个元素的自身。
45 //就行这里的writeChars方法一样,他翻转了每个元素自身的存储方式,但是在数组中的位置没有改变
46 public final void writeChars(String s) throws IOException {
47 int len = s.length();
48 for (int i = 0; i < len; i++) {
49 writeChar(s.charAt(i));
50 }
51 }
52 public final void writeDouble(double v) throws IOException {
53 writeLong(Double.doubleToLongBit