Python&Flash开发斗兽棋---2.1网络数据打包与解包

7 篇文章 0 订阅
6 篇文章 0 订阅

既然选择了原生态的SOCKET,那么首先要面对的问题就是数据的打包/解包问题。

对于服务器端,我们使用struct作为数据打包/解包工具,并对struct作了简单了解(相关资料:Python模块学习 ---- struct 数据格式转换)。

在Flash客户端,我们数据处理使用的是ByteArray类。

简单统计一下,我们在开发过程中可能用到的数据类型,在struct中的表现手法,以及相对于的Python数据类型和Flash数据类型,还有Flash中数据解包读取方式

数据类型PythonFlashFlash读取方法struct标识符自定义标识符
字符串StringStringreadUTFBytes(len:uint):String*ss
16bit有符合整型intintreadShort():inthh
16bit无符号整型intuintreadUnsignedShort():uintHH
32bit有符号整型intintreadInt():intii
32bit无符号整型intuintreadUnsignedInt():uintII
64bit浮点型floatNumberreadFloat():Numberdd

需要提一下的是,所有的字符串采用的是UTF-8编码。


数据包定义

数据长度+协议号+数据段

其中:

数据长度:长度为4字节的无符号整形数据,用来指定数据段长度

协议号:长度为2字节的ushort数字

数据段:用来存放需要传递的二进制数据


对于Python服务器端,我们发现struct使用起来还是挺不方便的,特别是字符串和包格式定义符。所以对它进行简单封装

import struct
import struct

datafmt = {
    101 : 'sH',     # redirect address
    102 : 's',      # server message
    
    1001 : 'ss',    # login : name|password
    
    2001 : 'H',     # login result: 0 success or fail
    2002 : 'Is',    # user enter : user id | user name
    2003 : 'HH',    # animal load : type | animal model id
                    # type   : 1  2  3  4  5  6  7  8
                    # animal : 象 狮 虎 豹 犬 狼 猫 鼠
    2004 : 'Hsdsis',# Test
}

def pack(comid, data):
    print comid, data
    global datafmt
    if comid not in datafmt:
        print cmd_id, ' not in ', datafmt
        return ''
    
    fmtStr = datafmt[comid]
    fmtStrRes = []
    idx = 0
    fixString ={}
    for k in fmtStr:
        # 计算字符串长度
        if k == 's':
            _strLength = len(data[idx])
            fixString[idx] = _strLength
            fmtStrRes.append('H'+ str(_strLength) +'s')
        else:
            fmtStrRes.append(k)
        idx = idx + 1

    # 指定BIG_ENDIAN模式
    fmt = '>H' + ''.join(fmtStrRes)

    # 插入字符串长度值
    if len(fixString)>0:
        idx = 0
        for k,v in fixString.items():
            data.insert(k+idx,v)
            idx = idx + 1

    # 加入协议号
    data.insert(0, comid)

    # 打包结果
    result = struct.pack(fmt, *data)

    # 返回结果,加上数据长度
    return struct.pack('>I', len(result) - 2) + result

def unpack(comid, data):
    print 'unpack----------', len(data)
    global datafmt
    if comid not in datafmt:
        print cmd_id, ' not in ', datafmt
        return None

    fmtStr = datafmt[comid]
    fmtStrRes = []
    _data_index = 0
    for k in fmtStr:
        if k == 'h' or k == 'H':
            _data_index += 2
            fmtStrRes.append(k)
        elif k == 'i' or k == 'I':
            _data_index += 4
            fmtStrRes.append(k)
        elif k == 'd':
            _data_index += 8
            fmtStrRes.append(k)
        elif k == 's':
            _string_length = data[_data_index:_data_index + 2]
            print repr(_string_length)
            _strlen, = struct.unpack('>H', _string_length)
            print _strlen
            data = data[:_data_index] + data[_data_index + 2:];
            _data_index += _strlen
            fmtStrRes.append(str(_strlen) + 's')

    # 指定BIG_ENDIAN模式
    fmt = '>' + ''.join(fmtStrRes)
    print 'unpack fmt=', fmt

    # 获取结果
    print 'data.len = ', len(data)
    return struct.unpack(fmt, data)


我们定义了pack和unpack两个函数,分别用于打包和解包。相应的测试代码如下:

if __name__=='__main__':
    # Test
    _pack_result = pack(2004, [1, '123456', 5.0, '789', 1000, '0'])

    if(len(_pack_result) > 6):
        # 获取数据段长度
        _data_len = struct.unpack('>I', _pack_result[:4])[0]
        print 'data len = ', _data_len

        if(len(_pack_result) >= _data_len + 6):
            # 获取协议号
            _comid = struct.unpack('>H', _pack_result[4:6])[0]
            print 'command id = ', _comid

            # 获取数据段
            _data = _pack_result[6:_data_len + 6]

            # 解包
            print unpack(_comid, _data)

            # 源数据截除
            _pack_result = _pack_result[_data_len + 6:]
            print 'left len = ', len(_pack_result)


对于Flash客户端,我们一样需要定义pack和unpack方法

package
{
	import flash.utils.ByteArray;

	public class NetCommand
	{
		private static var s_Format:Object = 
			{
				101 : 'sH',		// redirect address
				102 : 's',		// server message
				
				1001 : 'ss',	// login : name|password
				
				2001 : 'H',		// login result: 0 success or fail
				2002 : 'Is',	// user enter : user id | user name
				2003 : 'HH',	// animal load : type | animal model id
								// type   : 1  2  3  4  5  6  7  8
								// animal : 象 狮 虎 豹 犬 狼 猫 鼠
				2004 : 'Hsdsis'// Test
			}
		
		public static function Pack(comid:uint, raw_data:Array) : ByteArray
		{
			// 检查协议号
			if(! comid in s_Format)
			{
				throw new Error("出现未知协议号:" + comid);
				return null;
			}
			
			// 构建数据段
			var data : ByteArray = new ByteArray;
			for(var i:int = 0; i < s_Format[comid].length; ++i)
			{
				trace(s_Format[101]);
				trace(s_Format[comid]);
				var flag:String = s_Format[comid].charAt(i);
				if(flag == 'h')
				{
					data.writeShort(raw_data[i]);
				}
				else if(flag == 'H')
				{
					data.writeShort(raw_data[i]);
				}
				else if(flag == 'i')
				{
					data.writeInt(raw_data[i]);
				}
				else if(flag == 'I')
				{
					data.writeUnsignedInt(raw_data[i]);
				}
				else if(flag == 'd')
				{
					data.writeDouble(raw_data[i]);
				}
				else if(flag == 's')
				{
					data.writeUTF(raw_data[i]);
				}
			}
			
			// 构建结果
			var pack:ByteArray = new ByteArray;
			
			// 写入数据段长度
			pack.writeUnsignedInt(data.length);
		
			// 写入协议号
			pack.writeShort(comid);
			
			// 写入数据段
			pack.writeBytes(data);
			
			return pack;
		}
		
		public static function Unpack(comid:uint, data:ByteArray) : Array
		{
			// 检查协议号
			if(! comid in s_Format)
			{
				throw new Error("出现未知协议号:" + comid);
				return null;
			}
			
			// 解析数据
			var raw_data:Array = new Array;
			for(var i:int = 0; i < s_Format[comid].length; ++i)
			{
				var flag:String = s_Format[comid].charAt(i);
				if(flag == 'h')
				{
					raw_data.push(data.readShort());
				}
				else if(flag == 'H')
				{
					raw_data.push(data.readUnsignedShort());
				}
				else if(flag == 'i')
				{
					raw_data.push(data.readInt());
				}
				else if(flag == 'I')
				{
					raw_data.push(data.readUnsignedInt());
				}
				else if(flag == 'd')
				{
					raw_data.push(data.readDouble());
				}
				else if(flag == 's')
				{
					raw_data.push(data.readUTF());
				}
			}
			
			return raw_data;
		}
	}
}

测试代码如下:

// Test
var arr:Array = new Array;
arr.push(1);
arr.push("123456");
arr.push(5.0);
arr.push('789');
arr.push(1000);
arr.push('0');
trace(arr);
var result:ByteArray = NetCommand.Pack(2004, arr);

result.position = 0;
if(result.bytesAvailable > 6)
{
	var data_len:uint = result.readUnsignedInt();
	var comid:uint = result.readUnsignedShort();
	if(result.bytesAvailable >= data_len)
	{
		
		var raw_data:Array = NetCommand.Unpack(comid, result);
		trace(raw_data);
	}
}

最后,我们需要对Python服务器端和Flash客户端进行联机测试。

Python服务器端代码

def dataReceived(self, data):
        if(len(data) > 6):
            # 获取数据段长度
            _data_len = struct.unpack('>I', data[:4])[0]

            if(len(data) >= _data_len + 6):
                # 获取协议号
                _comid = struct.unpack('>H', data[4:6])[0]

                # 获取数据段
                _data = data[6:_data_len + 6]

                # 解包
                _raw_data = unpack(_comid, _data)
                print '_raw_data = ', _raw_data

                # 构建回发数据
                _data = []
                for d in _raw_data:
                    print '_data = ', _data
                    _data.insert(len(_data), d)

                # 回发数据
                _pack_result = pack(_comid, _data)
                self.transport.write(_pack_result)
            else:
                print '数据不完整,请等待...'
        else:
            print '目前没有可用数据...'

Flash客户端代码

private function onMouseClick(e:MouseEvent):void
{
	// 发送数据
	var arr:Array = new Array;
	arr.push(1);
	arr.push("123456");
	arr.push(5.0);
	arr.push('789');
	arr.push(1000);
	arr.push('0');
	trace(arr);
	var result:ByteArray = NetCommand.Pack(2004, arr);
	m_Socket.writeBytes(result);
	this.m_Socket.flush();
}

private function socketDataHandler(event:ProgressEvent):void
{
	this.readBytes(this.m_ReadBuff, this.m_ReadBuff.length);
	if(m_ReadBuff.bytesAvailable > 6)  
	{
		var data_len:uint = m_ReadBuff.readUnsignedInt();  
		var comid:uint = m_ReadBuff.readUnsignedShort();  
		if(m_ReadBuff.bytesAvailable >= data_len)  
		{  
			
			var raw_data:Array = NetCommand.Unpack(comid, m_ReadBuff);  
			trace(raw_data);  
		}  
	}
}


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ctbinzi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值