微信蓝牙外设协议03beta.docx

上传人:小飞机 文档编号:2022742 上传时间:2022-12-31 格式:DOCX 页数:40 大小:542.81KB
返回 下载 相关 举报
微信蓝牙外设协议03beta.docx_第1页
第1页 / 共40页
微信蓝牙外设协议03beta.docx_第2页
第2页 / 共40页
微信蓝牙外设协议03beta.docx_第3页
第3页 / 共40页
微信蓝牙外设协议03beta.docx_第4页
第4页 / 共40页
微信蓝牙外设协议03beta.docx_第5页
第5页 / 共40页
点击查看更多>>
资源描述

《微信蓝牙外设协议03beta.docx》由会员分享,可在线阅读,更多相关《微信蓝牙外设协议03beta.docx(40页珍藏版)》请在三一办公上搜索。

1、微信蓝牙外设协议Project BlueShadow V1.0.3Tencent Confidential文档变更日志版本变更日期01初稿2013/10/902针对常见问题,增加说明2013/10/1803针对常见问题,增加说明。修改包头结构。2013/11/504第二期初稿增加17条protobuf协议,废弃1条,修改1条2013/11/2605暂时去掉二期的内容(17条protobuf),在一期的基础上增加三条协议,并修改auth协议。加入加密解密部分说明。2014/2/1206修改uuid的规定。补充协议字段和错误码。补充加密部分细节。2014/2/2707修改加密协议,增加例子。补充不

2、加密的协议。补充错误码。2014/3/407 . 1修改proto里的UserId,Challeange字段。2014/3/707 . 2规定低功耗蓝牙广播包必须包含mac地址。InitResp增加可选字段以支持自动同步模式,修改解码失败错误码。2014/4/307 . 3修改SendData, SendDataPush两条协议,增加type字段,用来区分是发送给厂商服务器,还是发送给公众平台服务器。2014/4/240 . 7 . 4修改ble蓝牙service uuid的值。2014/4/250 . 7 . 5公众平台协议手环修改:增加BaseResponse字段。2014/4/301 .

3、 0 . 1提升版本号为1.0.1 Beta2014/6/91 . 0 . 2增加Read Characteristics,以支持ios多app连接。增加html jsapi支持。公众平台协议手环修改:增加rtc时间支持。废弃一些字段,修改协议名字为SendData,RecvData等。2014/7/151 . 0 . 3增加蓝牙扫描绑定相关规范。2014/8/18目录概要5整体架构5主要功能5蓝牙BLE模拟成流6协议71 绑定7扫码绑定8蓝牙扫描绑定82 扫描和连接93 流94 包104 包结构105 定长包头116 变长包体117 身份验证和加密128 不加密的方法149 会话约定1510

4、 时序1511 Read Characteristics1512 其他16微信的Protobuf协议161 概述162 命令列表163 错误码174 JSAPI19函数19事件21附录221 ProtoBuf协议介绍222 蓝牙硬件一些规定233 Ios BLE设备的截图234 包的二进制例子255 包的数据流图266 包的时序图277 加解密字段288 Md5DeviceTypeAndDeviceId的例子299 CBC例子3010 CRC32例子3111 微信蓝牙外设proto文件3112 微信公众平台proto文件3513 JsApi例子37概要该文档规范了蓝牙设备和手机上的微信的通信协

5、议协议支持经典蓝牙和4.0 BLE蓝牙,目前支持ios和andriod两个系统,后续会扩展到其他系统。整体架构厂商服务器和外设,由厂商开发完成。微信会提供服务器的接口以对接厂商的服务器,会提供手机的接口(如本文规定的蓝牙协议)以对接厂商的外设。主要功能该协议打通了设备和厂商服务器之间的数据链路,也就是支持将设备上的数据发送到厂商的服务器上,也支持将厂商的数据发送到设备。厂商的数据对于微信来说,是黑盒,微信不对设备数据做分析。该协议也打通了设备和微信服务器之间的数据链路。设备和微信服务器之间的数据格式由微信规定,例如登录,新消息通知等。蓝牙BLE模拟成流微信支持蓝牙BLE。微信规定了蓝牙BLE设

6、备需要先模拟成流(即stream,输入输出流)。经典蓝牙的RFCOMM,就是一个流。流具有的特性有:a. 可以传输无限长度的数据 b. 双工,读写可以并发,互不干扰。显然,蓝牙BLE无法传输无限长度的数据,为了实现这个目的,需要定义一个规范。蓝牙设备需暴露两个特征值(Characteristics):Write特征值,Indication特征值。蓝牙设备从Write特征值接受数据,从Indication特征值发送数据。Indication特征值类型是bytes。这里我们约定,把一个特征值一次传输的数据,称为一帧(不同类型的特征值一次传输的数据长度是不一样的)。注意:应用层上的数据包(例如1k大

7、小),会分散成许多帧来传输。蓝牙设备写过程:1. 分帧:假设蓝牙手环上有1k数据,要发给手机微信。由于一个特征值长度有限(如20个字节),显然需要分多次才能传输完成。1k数据,要分成1024字节/ 20字节=51 个帧。剩下的4个字节,不足一帧(20个字节),需补齐为一帧并对剩下的16个字节赋0。总共是52帧。2. 发送第一个帧:把第一个帧的内容放入特征值里面。然后通知手机读取数据,通知有两种方式,Indication 和notify,这里使用Indication方式,即带响应的通知。当通知完成的时候,可以认为手机已经读完数据。这就完成了发送第一个帧。3. 按照2的步骤,依次发送剩下的帧。蓝牙

8、设备读过程:当蓝牙设备发现读特征值收到数据的时候,就接收数据,并追加到设备的buf里。注意:蓝牙设备必须等微信app订阅了Characteristics之后,才能indicate数据,否者会造成设备发送数据丢失的问题。协议1 绑定用户绑定设备有两种方式:扫码和蓝牙扫描。这两种方式都需要先向微信公众平台注册授权设备(具体api参见公众平台文档)。扫码绑定用户通过扫描设备二维码绑定设备(获取二维码的方法见公众平台文档)。用户场景:用户打开扫一扫界面,扫码设备二维码,出现公众号页面。用户点击关注,进行绑定设备。扫码绑定并不需要设备在身边。蓝牙扫描绑定用户通过扫描周边的蓝牙设备进行绑定。用户场景(可确

9、认设备):用户先关注公众号,点击绑定设备,进入绑定说明页。同时,微信开始扫描设备。用户操作设备确认(如点击设备上的按钮,或者双击设备),设备广播特殊的包(称为确认包),微信监听到包之后,开始关注并绑定。蓝牙扫描绑定需要设备在身边。蓝牙扫描绑定的设备分为两种:可确认(如有按钮,或者可以双击)的设备。大部分设备属于可确认设备。无法确认的设备(无法确认的设备,出于安全性的考虑,可用扫码绑定)可确认的设备,当用户进行设备确认时(如双击手环,或者按按钮),需要广播特殊的包(称为确认包,具体格式见附录)。2 扫描和连接进入特定界面后,微信会开始扫描设备。低功耗蓝牙设备需要在广播包里广播:a. 微信规定的s

10、ervice uuid(具体见附录)。b. 厂商自定义字段里,包含mac地址(具体见附录)。并且需要包含指定的Characteristics(具体见附录)。Andriod经典蓝牙设备需要暴露一个指定uuid的rfcomm服务(具体见附录)。Ios经典蓝牙需要通过mfi认证,并且SerialNumber需为mac地址(字符串形式)。扫描到设备后,微信会连接设备(Ios经典蓝牙需要用户手动在设置界面里面连接上设备)。3 流经典蓝牙使用RFCOMM通信(是个流),蓝牙BLE也模拟成流。4 包流之上运载的是一个紧接着一个的业务逻辑的数据包。数据包的发送方和接受方:设备厂商服务器,或者设备微信服务器。把

11、设备-厂商服务器/微信服务器的请求称为Req,回包称为Resp。一个请求,对应着一个回包。把厂商服务器/微信服务器-设备的请求称为PushReq,没有回包(即没有PushResp这样的包)。4 包结构由定长包头和变长包体组成。变长包体Protoalbuf打包的二进制数据定长包头struct BpFixHeadunsigned char bMagicNumber;unsigned char bVer;unsigned short nLength;unsigned short nCmdId;unsigned short nSeq;包包的二进制例子见附录。5 定长包头字段类型说明bMagicNumb

12、erunsigned char填0xFEbVerunsigned char包格式版本号,填1nLengthunsigned short为包头+包体的长度nCmdIdunsigned short命令号,如ECI_req_auth,ECI_resp_sendDataToManufacturerSvr等nSequnsigned short递增。一个Req对应一个Resp,并且它们的nSeq相同,并且永不为0。Push的nSeq永远为0;6 变长包体为Protobuf(protobuf的介绍见附录)打包的结构。例如AuthReq。一个包体里面只有一个Req,或者一个Resp,或者一个PushReq。每

13、个Req/Resp/PushReq都有对应的EmCmdId,例如AuthReq的命令号为ECI_req_auth。具体的定义见附录。7 身份验证和加密为保证数据安全,所有命令都需要加密。加密算法选用aes 128位,并使用cbc模式,pkcs7填充。初始向量为密钥。具体验证和加密的步骤如下:a. 设备需要烧一个Key(128位)到硬件上,微信服务器也要记录下这个Key。一个设备(deiveId+deviceType唯一确定一个设备),对应一个Key。key要保护好(类似用户密码,银行卡账号密码),千万不要印刷出来或者打印出来。b. 对于设备来说,手机和服务器可看成一个黑盒。设备和手机服务器之间

14、,通过Auth命令,使用key,最终,把sessionKey下发给到设备。Auth的步骤为:设备发送AuthReq,里面的字段有MD5(deviceType+deviceId)(如果设备算md5很麻烦的话,建议先算好,直接烧进设备里面),另一个字段为AES加密的一段buffer(具体字段细节在附录描叙)。微信发送回包AuthResp,里面的字段有用AES加密的含有sessionkey的一段buffer(具体字段细节在附录描叙)。设备解密这段buffer可得到SessionKey。c. 设备和手机之间,就通过sessionKey来加解密包。AuthReq, AuthResp之后的所有命令,例如I

15、nitReq, InitResponse,SendDataToManufaturSvrReq,SendDataToManufacturerSvrResponse,ManufacturerSvrSendDataPush等都需要加解密。d. 加密只针对变长包体,不需要加密定长包头。e. 为了提高安全性,设备可再进一步的验证手机是否可信。设备在InitReq里填入Challeange(一个4字节的随机数random4),手机将在InitResp里面返回ChalleangeAnswer(一个4字节的数,值为crc32(random4))。设备验证ChalleangeAnswer是否正确。如果是,说明手

16、机是可信的。身份验证和加解密大体的流程图为:另外有一些细节:a. 当sessionKey过期,手机对设备的所有请求都返回错误码 EEC_sessionTimeout,要求设备重做一次auth。b. 当发现解密设备的包失败时,返回 EEC_decode。这种情况下,通常是sessionkey过期,或者是设备程序有问题。8 不加密的方法为了降低设备厂商的接入难度,加快产品进程,微信客户端同时支持不加密的方式。但是,这个只是在测试阶段可用,正式的设备发布出去的时候,都要求加密。步骤如下:将AuthReq里的AesSign字段付空。发送AuthReq。收到AuthResp之后,忽略掉AesSessio

17、nKey字段。将InitReq里的Challeange字段随便付一个值。发送InitReq。收到InitResp,忽略掉ChalleangeAnswer字段。随后的包都不需要加解密。9 会话约定a. 设备连上微信之后,需要发送AuthReq,等收到成功的回包之后,接着还要发送InitReq,并收到成功的回包之后,才能正常发送数据。如果设备没有auth,手机对设备的所有请求都返回错误码 EEC_needAuth。b. 当出现解包异常的时候,直接断开连接。c. Push类的包seq永远为0。Req类和Resp类的包的seq永不为0。d. 服务器可随时发送Push包。e. 厂商服务器发送的Push包

18、(注意Push包是没有回包的,即没有PushResp),如果需要设备的回包,需要由厂商自己实现。具体方法如:厂商发送ManufacturerSvrSendDataPush给设备,设备收到push后,向厂商服务器发送一个SendDataToManufatureSvrReq。这时厂商服务器可知道设备收到了push,并且可以从Req里取得设备的回应数据。10 时序时序图请见附录。11 Read Characteristics设备需要再微信的service下面,暴露一个read character,内容为6字节的mac地址。当ios上的其他app连接上设备时,设备不会再广播,微信会读取该特征值,以确定

19、是否要连接该设备。12 其他目前规定了一些基础的协议,更多的协议等待补充。微信的Protobuf协议1 概述变长包体部分,使用的是Protobuf定义的协议。Protobuf文件见附录。里面规定了设备和微信客户端之间的命令。2 命令列表名称描叙Auth登录Init初始化SendData设备发送数据给厂商或微信公众平台或微信客户端。当type为空或者等于0时,表示发送给厂商服务器。当type为10001时,表示发送给微信客户端html5设备会话界面。当type为其他时,表示发送给公众平台服务器。具体的定义请看 附录:微信公众平台proto文件。举个例子,type等于1时,表示手环数据。RecvD

20、ataPush厂商或微信客户端或微信公众平台发送数据给设备当type为空或者等于0时,表示厂商发送设备。当type为10001时,表示收到微信客户端html5设备会话界面的数据。当type为其他时,表示公众平台发送给设备。具体的定义请看 附录:微信公众平台proto文件。举个例子,type等于1时,表示手环数据。SwitchViewPush微信客户端进入退出界面的通知SwitchBackgroudPush微信客户端进入退出后台的通知3 错误码Proto里的错误码EEC_system微信客户端一般的错误EEC_needAuth设备未登录。需要登录。EEC_sessionTimeoutsessio

21、nKey超时。需要重新登录。EEC_decode微信客户端解proto失败。可能是设备端打包代码有bug。EEC_deviceIsBlock微信客户端一段时间之内禁止设备的请求。通常是设备某些异常行为引起,如短时间多次登录,大量发送数据等。EEC_serviceUnAvalibleInBackgroundios处于后台模式,无法正常服务EEC_deviceProtoVersionNeedUpdate设备的proto版本过老,需要更新EEC_phoneProtoVersionNeedUpdate微信客户端的proto版本过老,需要更新EEC_maxReqInQueue设备发送了多个请求,并且没有

22、收到回包。微信客户端请求队列拥塞。EEC_userExitWxAccount用户退出微信帐号。为正数时具体见微信公众平台。固定包头里的错误码:固定包头里的错误码放在cmdid字段里。当设备收到这样的错误码后,可以通过seq查出是那个命令失败。目前只有一个错误码。ECI_err_decode微信客户端解密包体失败。通常是因为sessionKey过时,需要重新做一次auth,也可能是设备加密代码有bug。4 JSAPI微信提供javascript api,允许从Html页面直接获取设备状态,并收发设备的数据,而不需要经过服务器。实时性要求高的蓝牙设备(例如遥控汽车)可采用这种方式。当SendDat

23、a的type为EDDT_wxDeviceHtmlChatView时,表示设备发送数据给Html页面。当RecvDataPush的type为EDDT_wxDeviceHtmlChatView设备收到html发过来的数据。Jsapi包括函数和事件两部分。具体如下:函数函数名hdInit描叙初始化设备库参数无返回值err_msg: hdInit:ok 成功, hdInit:fail 失败minVersion:当前微信客户端支持的最小jsapi版本maxVersion:当前微信客户端支持的最大jsapi版本函数名hdRelease描叙关闭设备库参数无返回值err_msg:hdRelease:ok 成功

24、, hdRelease:fail 失败。函数名hdGetDeviceInfos描叙获取设备信息参数无返回值err_msg:hdGetDeviceInfos:ok 成功, hdGetDeviceInfos:fail 失败。deviceInfos:deviceInfos : deviceId:myDevice1, state:connected, deviceId:myDevice2, state:connected deviceInfos包含多个deviceInfo,每个deviceInfo形如:deviceId:myDevice1, state:connected。函数名hdSendDataT

25、oDevice描叙发送数据给设备参数deviceId(必填):设备iddata(必填):数据,经过base64编码后的字符串。返回值err_msg:hdSendDataToDevice:ok 成功, hdSendDataToDevice:fail 失败。事件 事件名hdOnReceveDataFromDevice描叙接收到设备数据参数deviceId:设备iddata:数据,经过base64编码后的字符串。事件名hdOnDeviceStateChanged描叙接收到设备数据参数deviceId:设备idstate:connecting 连接中, connected 连接上,disconnect

26、ed 连接断开。使用例子见附录。附录1 ProtoBuf协议介绍ProtoBuf是google提供的一套开源的软件协议。它主要作用是把c/c+的struct打包成为二进制数据,或者把二进制数据解包成c/c+的struct。具体使用过程为:1. 定义proto文件2. 通过工具把.proto文件编译成.h, .c文件(里面包含struct和函数) 3 调用.h文件里的封包解包函数官方网站为:其他相关例子和工具见附件。2 蓝牙硬件一些规定名称值ServiceUUID0xFEE7(该uuid经蓝牙官方授权)Write Characteristics UUID0xFEC7Indicate Charac

27、teristics UUID0xFEC8Read Characteristics UUID0xFEC9Andriod RFCOMM UUIDe5b152ed-6b46-09e9-4678-665e9a972cbcIos经典蓝牙的iap层的SerialNumber必须赋值,且等于mac地址(字符串形式)。低功耗蓝牙的广播包的manufature specific data里必须包含mac地址(6字节),并且以mac地址结尾。并且manufature specific data长度需大于等于8字节(最前两个字节为company id,没有的话随便填)。可确认设备的确认包的manufature sp

28、ecific data需以下面格式结尾:0xfe 0x01 0x01 + mac地址(6字节)3 Ios BLE设备的截图LightBlue是一个Iphone上的app。这是设备的截屏。请下载App并检查: 1. serviceId是否正确 2. characteristics是否正确(包括uuid和属性) 3. 是否在manufature data最尾部带上了mac地址。 4 包的二进制例子Req接受两种格式:20字节对齐补零格式,不对齐格式。以AuthReq为例子(不加密):1. 20字节对齐补零(ble特征值有些芯片只能发送固定20个字节,20字节剩下的部分补零)fe(MagicNumb

29、er)01(版本号)00 3b(总长度) 2711(命令号)0001(Seq)(变长包体:0a 00 12 10 b4 3f 12 04 2a 02 e0 1c 2b dd 7d 02 90 62 13 a3 18 80 80 04 20 01 28 01 32 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 03 41 4d 33)(补齐:00)注意:这里的包长度为003b(即59字节),由于20字节对齐,导致总长为60字节。2. 不对齐(经典蓝牙没有对齐问题,某些ble芯片也没有对齐问题)fe(MagicNumber)01(版本号

30、)00 3b(总长度) 2711(命令号) 0001(Seq) (变长包体:0a 00 12 10 b4 3f 12 04 2a 02 e0 1c 2b dd 7d 02 90 62 13 a3 18 80 80 04 20 01 28 01 32 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 62 03 41 4d 33)以AuthResp为例子:fe(MagicNumber)01(版本号)000e(总长度) 4e21(命令号)0001(Seq) (包体:0a020800 1200)5 包的数据流图6 包的时序图最简单的时序图:Push

31、包插入的时序图多Req包并且有push包插入的时序图:注意:多个Req包时,到达厂商服务器的先后顺序不能保证。同样多Resp包的情况下,到达外设的顺序也不能保证。7 加解密字段AuthReq里的AesSign字段 = AES(Ran | Seq | CRC32 ( DeviceId | Ran | Seq ))a) 设备生产一个随机数Ran ( 4字节 ),注意:随机数应尽量随机b) 若设备有能力获得当前的时间戳,则使用时间戳作为Seq ( 4字节 ),否则,使用一个从0开始递增的序列号,保证每次auth的Seq递增c) 将Ran和Seq拼接到DeviceId(不含DeviceType)后得到

32、二进制串1(变长)d) 计算二进制串1的CRC32 ( 4字节 )e) 将Ran、Seq、CRC值按顺序拼接得到12字节的二进制串2,使用设备key对二进制传2进行AES加密,得到16字节的AesSignAuthResp里的AesSessionKey字段 = AES (SessionKey )设备auth成功后将取得一个32字节的AesSessionKey字段,使用设备key做AES解密后可得到16字节的明文内容.该内容为sessionKey。InitReq里的Challeange字段为随机数(4字节,记为Random3)InitResp里的ChalleangeAnswer字段为CRC32(R

33、andom3)。上述身份认证和加密的方式为0x1版本,对应AuthReq里面的AuthMethod字段。该版本未来可能升级。8 Md5DeviceTypeAndDeviceId的例子Md5DeviceTypeAndDeviceId为16字节二进制数据。假设deviceType为:gh_d53f87f298e5,deviceId为:test_device。将deviceType+deviceId拼起来,不算0:md5(gh_d53f87f298e5test_device,32) = 0x26cdd942b8ee68b022cc53bba16c70399 CBC例子Key 为字符串 “314159

34、2653589793”Iv和Key相同。数据 : CBC(pkcs7)结果 : 数据: CBC(pkcs7)结果 : 数据: CBC(pkcs7) 结果:10 CRC32例子多项式为:0xedb88320L字符串test_device_ios的crc32为:0x02e312f3。11 微信蓝牙外设proto文件/ proto version: 1.0.2package MmBp;enum EmCmdId ECI_none = 0; / req: 蓝牙设备 - 微信/厂商服务器 ECI_req_auth = 10001; / 登录 ECI_req_sendData = 10002; / 蓝牙设备

35、发送数据给微信或厂商 ECI_req_init = 10003; / 初始化 / resp:微信/厂商服务器 - 蓝牙设备 ECI_resp_auth = 20001; ECI_resp_sendData = 20002; ECI_resp_init = 20003; / push:微信/厂商服务器 - 蓝牙设备 ECI_push_recvData = 30001; / 微信或厂商发送数据给蓝牙设备 ECI_push_switchView = 30002; / 进入/退出界面 ECI_push_switchBackgroud = 30003; / 切换后台 ECI_err_decode = 2

36、9999; / 解密失败的错误码。注意:这不是cmdid。为节省固定包头大小,这种特殊的错误码放在包头的cmdid字段。enum EmErrorCode EEC_system = -1; / 通用的错误 EEC_needAuth = -2; / 设备未登录 EEC_sessionTimeout = -3; / session超时,需要重新登录 EEC_decode = -4; / proto解码失败 EEC_deviceIsBlock = -5; / 设备出现异常,导致被微信临时性禁止登录 EEC_serviceUnAvalibleInBackground = -6; / ios处于后台模式,

37、无法正常服务 EEC_deviceProtoVersionNeedUpdate = -7; / 设备的proto版本过老,需要更新 EEC_phoneProtoVersionNeedUpdate = -8; / 微信客户端的proto版本过老,需要更新 EEC_maxReqInQueue = -9; / 设备发送了多个请求,并且没有收到回包。微信客户端请求队列拥塞。 EEC_userExitWxAccount = -10; / 用户退出微信帐号。message BaseRequest message BaseResponse required int32 ErrCode = 1; option

38、al string ErrMsg = 2;message BasePush / req, resp =/ 登录 -message AuthRequest required BaseRequest BaseRequest = 1; required bytes Md5DeviceTypeAndDeviceId = 2; / deviceType加deviceId的md5,16字节的二进制数据 required int32 ProtoVersion = 3; / 设备支持的本proto文件的版本号,第一个字节表示最小版本,第二个字节表示小版本,第三字节表示大版本。版本号为1.0.0的话,应该填:0

39、x010000;1.2.3的话,填成0x010203。 required int32 AuthProto = 4; / 填1 required int32 AuthMethod = 5; / 验证和加密的版本号,当前填1 required bytes AesSign = 6; / 具体生成方法见文档 optional string TimeZone = 10; / 废弃 optional string Language = 11; / 废弃 optional string DeviceName = 12; / 废弃message AuthResponse required BaseRespon

40、se BaseResponse = 1; required bytes AesSessionKey = 2;/ 初始化 -enum EmInitRespFieldFilter EIRFF_userNickName = 0x1; EIRFF_platformType = 0x2; EIRFF_model = 0x4; EIRFF_os = 0x8; EIRFF_time = 0x10; EIRFF_timeZone = 0x20; EIRFF_timeString = 0x40;/ 微信连接上设备时,处于什么情景enum EmInitScence EIS_deviceChat = 1; / 聊天

41、 EIS_autoSync = 2; / 自动同步message InitRequest required BaseRequest BaseRequest = 1; optional bytes RespFieldFilter = 2; / 当一个bit被设置就表示要resp的某个字段:见EmInitRespFieldFilter。 optional bytes Challenge = 3; / 设备用来验证手机是否安全。为设备随机生成的四个字节。enum EmPlatformType EPT_ios = 1; EPT_andriod = 2; EPT_wp = 3; EPT_s60v3 = 4; E

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号