对网络包进行压缩可以减少网络带宽,提高传输效率, 很多时候一些报文中包含一些无符号整数,比如报文头中的长度信息.正常情况下一个无符号整数占用32位
从0x0000 0000 到 0xFFFF FFFF .
由于一个无符号整数始终占据着4字节内存,就算是1也会占据着4个字节,其实只要1字节就可以保存了.(要想压缩整数,基本算法思想是压缩在4字节之内也就是1,2,3,4.
把无符号整数进行逻辑分段:
0x00 ~ 0xFF 0~255 1Byte
0x100 ~ 0xFFFF 256~65535 2Byte
0x10000 ~ 0xFFFFFF 65536~16777215 3Byte
0x1000000 ~ 0xFFFFFFFF 16777216~4294967295 4Byt
通过上面可以大致看出1~4字节的逻辑分层.把无符号整数进行压缩在一个字符数组内,定义一个char buf[4];
当在0x00~0xFF之间时:
因为是只有1个字节只要保存在buf[0] 这个字符内。
当在0x100~0xFFFF之间时:
这个范围是2个字节,把前8位保存于数组buf[0]内,后8位保存于数组buf[1]内,通过位操作即可。
当在0x100~0xFFFFFF之间时也类似。
当在0x1000000 ~ 0xFFFFFFFF这个范围不进行压缩。因为必须占用4个字节,所以没什么意义。
下面看下压缩代码:
// 输入无符号整数data,压缩到1-4个字节后存入buf,返回压缩后的字节数
bool cb_compress(unsigned int data, char* buf, unsigned int& len) {
if (data <= 0xFF) {
buf[0] = (char)data;
len = 1;
} else if (data <= 0xFFFF) {
buf[0] = (char)((data & 0xFF00 )>> 8);
buf[1] = (char)((data & 0x00FF));
len = 2;
} else if (data <= 0xFFFFFF) {
buf[0] = (char)((data & 0x00FF0000) >> 16);
buf[1] = (char)((data & 0x0000FF00) >> 8);
buf[2] = (char)(data & 0x000000FF);
len = 3;
} else {
// 超过 0xFFFFFF 的不进行压缩
cout << "Invaild Data" << endl;
}
return TRUE;
}
对压缩过的数据进行解压缩,只要和压缩操作相反就可以了。不过要特别注意符号位的问题!
// 输入字节数组和字节数,输出一个整数data
bool cb_unCompress(char* buf, int len, unsigned int& data) {
if (3 == len) {
data = (unsigned int)(((buf[0] << 16) & 0x00FFFFFF)
| ((buf[1] << 8) & 0x00FFFF)
| (buf[2] & 0x0000FF) & 0x00FFFFFF);
} else if (2 == len) {
data = (unsigned int)((((buf[0] << 8) & 0x00FFFF))
| (buf[1] & 0x0000FF) & 0x00FFFF); // 0x00FF
} else if (1 == len){
data = (unsigned int)buf[0] & 0x000000FF;
} else {
cout << "unCompress Error" << endl;
}
return TRUE;
}
压缩图示:
如果你使用的Python,你可以从struct.pack这个方法进行入手。
比如:压缩2个字节:
buf = (c_char * 2)()
buf[0] = struct .pack("B", (data & 0xFF00) >> 8);
buf[1] = struct.pack("B", data & 0x00FF);
解压缩进行upack即可:
_data0 = stuct.unpack("B" , data[0])[0]
_data1 = struct.unpack("B", data[1])[0]
unpack_data = _data0 << 8 | _data1 & 0x00FF
还有种算法是通过每7位一保存,前面1位为符号位(非有符号的符号位,而是如果为0表示后面没有数据,1表示有数据)
bool cb_compress(unsigned int data, char *buf, unsigned int &len) {
len = 0;
// 32bit分成5段,每段7bit
for (int i = 4; i >= 0; --i) {
//得到第i段的数值
char c = (data >> (i*7)) & 0x7f;
if (c == 0x00 && !len) {
//最左的几段值为0,不保存
continue;
}
//最右段
if (i == 0) {
c &= 0x7f;
} else {//并非最右段,首bit标记为1表示后面有数据
c |= 0x80;
}
buf[len] = c;
len++;
}
//Invaild Data
if (!len) {
len++;
buf[0] = 0;
}
return TRUE;
}
// 评:上述压缩过程如果知道最终len是多少,那么只需要把最左非0段的首位bit置1即可,
// 在字符流的解码过程中,只要遇到了首位bit为1的段就知道是一个新的数据开始了。
如果检查为1 表示后面7位需要和下面一段数据进行拼接,0表示后面7位为最后7位。 所以可能压缩后位1~5字节内。
解压缩:
bool cb_unCompress(char *buf, int len, unsigned int &i) {
i = 0;
for (int index = 0; index < (int)len; ++index) {
char c = *(buf + index); //取出压缩数据
i = i << 7;
c &= 0x7f;
i |= c; //多于1位进行左移解压缩
}
return TRUE;
}
评:当把一个整数的bit串分成5段后,最左段是不够8bit的,那么对于int型的整数,可以把它当做uint型整数来压缩和解压缩。
Python实现压缩与解压缩:( 本段程序,参考http://www.cnblogs.com/AndersLiu/archive/2010/02/09/compressed-integer-in-metadata.html)
def Compress(self, data):
if data <= 0x7F:
bytes_1Arr = (c_char * 1)()
bytes_1Arr = struct.pack("B", data)
return bytes_1Arr
elif data <= 0x3FFF:
bytes_2Arr = (c_char * 2)()
bytes_2Arr[0] = struct.pack("B", (data & 0xFF00)>>8 | 0x80)
bytes_2Arr[1] = struct.pack("B", data & 0x00FF);
return bytes_2Arr
elif data <= 0xffff: bytes_3Arr = (c_char * 4)()
bytes_3Arr[0] = struct.pack("B", (data & 0xFF000000)>>24 | 0xC0)
bytes_3Arr[1] = struct.pack("B", (data & 0x00FF0000)>>16)
bytes_3Arr[2] = struct.pack("B", (data & 0x0000FF00)>>8)
bytes_3Arr[3] = struct.pack("B", (data & 0x000000FF))
return bytes_3Arr
else:
print "Invaild Data"
def unCompress(self, data):
_len = len(data)
if _len == 1:
return struct.unpack("B", data[0])
elif _len == 2:
_data0 = struct.unpack("B", data[0])
_data1 = struct.unpack("B", data[1])
return ((_data0[0] & 0x3F)<<8 | _data1[0]) & 0x00FF
elif _len == 4:
_data0 = struct.unpack("B", data[0])[0]
_data1 = struct.unpack("B", data[1])[0]
_data2 = struct.unpack("B", data[2])[0]
_data3 = struct.unpack("B", data[3])[0]
return ((_data0 & 0x1F)<<24 | _data1<<16 | _data2<<8 | _data3)
else:
print "unCompress Error!"
另外需要注意的是 Python返回后的是个元组 需要 用 .value来取值
图示:
-> 1000 0001 | 0000 0000