3.1 信息编码
3.1.1 基本整型
TCP和UDP套接字使我们能够发送和接收字节序列(数组),及范围在0~255之间的整数。
下面考虑发送一个byte型整数、一个short型整数、一个int型整数和一个long型整数,这四个类型在Java中依次用1、2、4、8个字节进行标示。
1. 发送顺序,可以由低位到高位发送(little-endian),也可以由高位到低位发送(big-endian)。
考虑厂整型123456787654321L,其16禁止标示形式为0x0000704885F926B
1.如果使用little-endian顺序传输这个整数,其字节的十进制数值序列为:
如果使用big-endian顺序传输这个整数,则其字节的十进制数值序列为:
对于任何一个多字节的整数,发送和接收方对于使用哪种传输顺序必须达成一致。
2. 发送者和接收者对于发送的数值是有符号的还是无符号的也要达成共识。Java中的四种基本整数类型都是有符号的。
下面的代码中演示了发送和接收整数。
package com.suifeng.tcpip.chapter3;
/**
* 基本整型的发送和接收(模拟)
*
* @author Administrator
*
*/
public class BruteForceCoding
{
// 要传输的参数
private static final byte byteVal = 101;
private static final short shortVale = 10001;
private static final int intVal = 100000001;
private static final long longVal = 1000000000001L;
// 每种类型所占的位数
private static final int BSIZE = Byte.SIZE / Byte.SIZE;
private static final int SSIZE = Short.SIZE / Byte.SIZE;
private static final int ISIZE = Integer.SIZE / Byte.SIZE;
private static final int LSIZE = Long.SIZE / Byte.SIZE;
// 淹没
private final static int BYTEMASK = 0xff;
/**
* 将要传输的二进制数据打印出来
*
* @param bArray
* 二进制数组
* @return
*/
public static String byteArray2DecimalString(byte[] bArray)
{
StringBuilder rbtn = new StringBuilder(256);
for (byte b : bArray)
{
rbtn.append(b & BYTEMASK).append(" ");
}
return rbtn.toString();
}
/**
* 将要发送的整数转换成二进制数组
*
* @param dst
* 目的二进制数组
* @param val
* 要传输的整数
* @param offset
* 位移
* @param size
* 所占字节数
* @return
*/
public static int encodeIntBigEndian(byte[] dst, long val, int offset,
int size)
{
for (int i = 0; i < size; i++)
{
dst[offset++] = (byte) (val >> (size - i - 1) * Byte.SIZE);
}
return offset;
}
/**
* 解析编码后的数据
*
* @param val
* 加密后的二进制数组
* @param offset
* 位移
* @param size
* 大小
* @return
*/
public static long decodeIntBigEndian(byte[] val, int offset, int size)
{
long rbtn = 0;
for (int i = 0; i < size; i++)
{
rbtn = (rbtn << Byte.SIZE) | ((long) val[offset + i] & BYTEMASK);
}
return rbtn;
}
public static void main(String[] args)
{
byte[] message = new byte[BSIZE + SSIZE + ISIZE + LSIZE];
// byte类型
int offset = encodeIntBigEndian(message, byteVal, 0, BSIZE);
// short类型
offset = encodeIntBigEndian(message, shortVale, offset, SSIZE);
// int类型
offset = encodeIntBigEndian(message, intVal, offset, ISIZE);
// long类型
offset = encodeIntBigEndian(message, longVal, offset, LSIZE);
System.out
.println("Encode message:" + byteArray2DecimalString(message));
// 解析加密后的数据
offset = 0;
// 解析byte
long value = decodeIntBigEndian(message, offset, BSIZE);
System.out.println("Byte Value=" + value);
// 解析short
offset += BSIZE;
value = decodeIntBigEndian(message, offset, SSIZE);
System.out.println("Short Value=" + value);
// 解析int
offset += SSIZE;
value = decodeIntBigEndian(message, offset, ISIZE);
System.out.println("Int value=" + value);
// 解析long
offset += ISIZE;
value = decodeIntBigEndian(message, offset, LSIZE);
System.out.println("Long value=" + value);
}
}
执行结果如下
如上图所示,运行过程中产生了一个字节序列
还有一一种方式可以使用Java提供的二进制的读写操作。
package com.suifeng.tcpip.chapter3;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
* 基本整型的发送和接收(模拟)
*
* @author Administrator
*
*/
public class BruteForceCoding2
{
// 要传输的参数
private static final byte byteVal = 101;
private static final short shortVal = 10001;
private static final int intVal = 100000001;
private static final long longVal = 1000000000001L;
public static void main(String[] args)
{
byte[] msg = null;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(buf);
System.out.println("要发送的数据:");
System.out.println("Byte Value="+byteVal);
System.out.println("Short Value="+shortVal);
System.out.println("Int Value="+intVal);
System.out.println("Long Value="+longVal);
try
{
out.writeByte(byteVal);
out.writeShort(shortVal);
out.writeInt(intVal);
out.writeLong(longVal);
out.flush();
msg = buf.toByteArray();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
try
{
if(buf != null)
{
buf.close();
}
if(out != null)
{
buf.close() ;
}
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println();
ByteArrayInputStream bais = new ByteArrayInputStream(msg);
DataInputStream dis = new DataInputStream(bais);
try
{
System.out.println("接收到的数据:");
System.out.println("Byte Value="+dis.readByte());
System.out.println("Short Value="+dis.readShort());
System.out.println("Int Value="+dis.readInt());
System.out.println("Long Value="+dis.readLong());
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
执行结果如下
3.1.2 字符串和文本
调用String的getByte方法可以返回一个根据平台默认字符集对String实例进行编码的字符数组。要保证一个字符串按照特定的字符集编码,只需要将字符集的名字传递给getBytes方法,其返回的自己数组就包含了指定字符集的字符串。
发送者和接收者必须在表示字符串的方式上达成共识。
3.1.3 位操作:布尔值编码
位图(bitmap)是对布尔信息进行编码的一种非常紧凑的方式,位图的主要思想是根据位图中的每一位都能够表示一个布尔值编码,通常是0表示false,1表示true。要操纵位图,可以考虑使用掩码,其中一位或者多位设为1,其他各位被清空。
下面是针对位图进行的操作
package com.suifeng.tcpip.chapter3;
/**
* 位图和掩码的操作
* @author Suifeng
*
*/
public class BitmapTest
{
private final static int BIT2 = (1 << 2); // 0000 0100
private final static int BIT3 = (1 << 3); // 0000 1000
private final static int BIT5 = (1 << 5); // 0010 0000
private final static int BIT7 = 0x80; // 0100 0000
private final static int BIT2_AND_BIT3 = 12; // 0000 0110
public static void main(String[] args)
{
int bitmap = 1234567;
System.out.println("将bitmap中的第五位置为1");
// 將bitmap中第五位置为1
bitmap |= BIT5;
System.out.println("第五位是否为1:"+(((bitmap & BIT5) & BIT5) != 0));
System.out.println();
System.out.println("将bitmap中的第七位置为0");
// 将bitmap中第七位置为1 取BIT7的补码,然后与原值按位与
bitmap &= ~BIT7;
System.out.println("第七位是否为1:"+(((bitmap & BIT7) & BIT7) == 0));
System.out.println();
System.out.println("将bitmap中的第二、三、五位置为0");
// 将bitmap第2、3、5位置为0
bitmap &= ~(bitmap & (BIT2_AND_BIT3 | BIT5));
System.out.println("第二位是否为0:"+(((bitmap & BIT2) & BIT2) == 0));
System.out.println("第三位是否为0:"+(((bitmap & BIT3) & BIT3) == 0));
System.out.println("第五位是否为0:"+(((bitmap & BIT5) & BIT5) == 0));
}
}
执行结果如下