• iOS开发之Socket通信实战--Request请求数据包编码模块



    实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索 这个库的用法,网上有很多资料,而且用法不难。

    在一些对Socket通信使用需求不是很高的应用中,比如需要多个iOS设备之间进行聊天 即时通讯,这时候只要用这个CocoaAsyncSocket就基本能满足这个需求,但是在本人从事的直播项目中对Socket通信协议的需求就比较高, 这个需求模式和游戏开发模式类似,因为游戏应用上就有很多数据变化是需要实时更新数据的,比如游戏的玩家生命值,如果在多人联网游戏平台上,你的设备上需 要实时更新其他玩家的生命值数据,而其他玩家设备上也会实时更新你的生命值数据,这个需求通过http主动请求是不能满足需求,所以游戏开发一般都会有 Socket通信。而在本人从事的直播项目后台所用的语言是erlang语言,后台对这Socket协议传输的数据有一个自定义的协议规则,例如下图:

                        图 1

    这协议就是一段可以在Socket传输的二进制流,后面第三部分协议数据流就是具体要传输的数据字段,

    而这个协议数据流内容就是如下通过erlang的一个协议文档的示例:

    // ========== 切换到新场景 ==========
    message Cs_20001{   
        uint8 SceneId = 1;          // 进入场景ID
        uint32 Line = 2;             // 分线,公共场景发0,多人副本后端会指定

     String message = 3;     // 消息
    }
    message Sc_20001{
        uint8 Res = 1;               // 1成功 0异常 2不能进入该关卡 3已经处于关卡当是 4没这个地图或关卡 5今日进入BOSS关或普通关次数上限 6体力不足 7进入条件不足 8地图类型不存在 10 没参加BOSS活动,无法进入 11 BOSS战房间状态已经结束 12 次数不足 13 冷却时间不足 14无权进入这个BOSS房间 15进BOSS房间条件不足
        uint32 SceneId = 2;          // 现在所在的场景
        uint8 Pos = 3;               // 坐标点
        uint8 Status = 4;            // 人物状态 0 正常 10队长状态
        uint8 Type = 5;              // 前端标识
        uint32 DefaultCombat = 6;    // 推荐战斗力

      String message = 7;     // 消息
    }
    先解释一下这个协议的一些定义:
      Cs就是Client --> Server(客户端向服务器发送的数据协议),而Sc就是Server-->Client(服务器向客户端返回的数据协议)

    本篇主要讲解请求模块,所以就讲这个Cs_20001请求协议封装数据包,这个20001就是这个协议的号,也叫协议ID(后面在代码中会用到)。

    在 Cs_20001中,有两个需要Socket发送给服务器的字段,都是uint32格式的,也是C语言的基本数据类型,但是在Socket传输中传输的是 二进制流,也就是说,我们需要将这两个uint32格式的数据字段转为二进制数据,然后拼接成一条数据流,然后让这个数据流在Socket中传递到服务 器,说到流如果学过Java的同学会对流的概念比较容易理解,如果没接触过流也没关系,就好比是一截水流从客户端流向服务器。其实这个数据流从客户端传递 到服务器,这个过程也不是想象的那么简单,涉及到很多底层的Socket传输逻辑逻辑,但是CocoaAsyncSocket已经做好了这部分的封装,而 且是OC面向对象的封装,我们只需要将需要传递的数据转为NSData通过CocoaAsyncSocket的代理方法传递过去就好了。

    对 于简单的需求,比如我仅仅只需要两台iPhone设备传递NSString字符串,只要将NSString转为NSData传递就好,但是对于我上面说的 erlang服务器需要自定义的协议,就需要客户端更多的封包解包的逻辑了,这个封包,比如拿上面字段协议为例子,就是将Cs_20001的数据包按照图 1中自定义的格式进行拼接数据,那么这时候在图 1中的需要两个字节的协议号就是20001了,也就是说需要将20001用两个字节的存储空间存储,然后在图 1中, 协议数据流的内容就是uint32 SceneId和uint32 Line这两个字段拼接成的数据,而且协议规定了顺序拼接就是怎么样的顺序,这里uint32 SceneId当然是在前而uint32 Line在后,拼接好后,可以计算得出,这个协议数据流的字节数?bytes,然后+2(协议号的字节长度),再+4(消息总长度需要的四个字节),就得 到整个协议流的长度,然后把这个总长度存储在四个字节的消息总长度中,当然整个协议流的从左到右的拼接顺序还是如图 1中所示,然后通过CocoaAsyncSocket传递给服务器就好。

    下面就通过代码来讲解这个业务逻辑:

    一、首先对后台提供的协议进行模型对象化,但凡有MVC基础就应该秒懂,其实就是MVC中的Model。

                                                                           图 2

    二、使用这个模型

                               图 3

    三、 因为在Socket通信协议中,是通过二进制字节码传输的,所以需要将模型中的属性,比如上面的sceneId、line和message分别转为 byte类型,然后转为NSData(OC端需要NSData),然后通过Socket传输。这个过程就叫做"编码(Encoded)",编码的同时还要 按顺序拼接,不要乱来哦。

                                图 4

    通过遍历并用运行时对模型对象的属性逐一取出类型和值,根据类型,来将这个值通过对应的编码方式来转为byte字节码,然后转为NSData这个OC的二进制对象类型。

    在这里属性的类型其实OC有规定,不了解可以通过上面的运行时进行打印出所有类型的结果。

    这里就上面那个Cs_20001的数据包模型对象编码的同时,也进行NSLog打印查看看是什么值和类型:

                      图 5

    看看这个结果,我们可以看到uint8_t类型是TC、uint32_t类型是TI、NSString类型是T@"NSString",当然还有很多其他的,可以自行去打印查看,或者Google搜索。

    那么接着就解释图 4中的70、73、76、79和82行的TYPE_...是什么了,其实就是常量定义:

                                   图 6

    而编码所用到的工具类的接口:

                                   图 7

    具体编码和解码的实现:

      1 #import "YMSocketUtils.h"
      2 
      3 @implementation YMSocketUtils
      4 
      5 /**
      6  *  反转字节序列
      7  *
      8  *  @param srcData 原始字节NSData
      9  *
     10  *  @return 反转序列后字节NSData
     11  */
     12 + (NSData *)dataWithReverse:(NSData *)srcData
     13 {
     14 //    NSMutableData *dstData = [[NSMutableData alloc] init];
     15 //    for (NSUInteger i=0; i<srcData.length; i++) {
     16 //        [dstData appendData:[srcData subdataWithRange:NSMakeRange(srcData.length-1-i, 1)]];
     17 //    }//for
     18     
     19     NSUInteger byteCount = srcData.length;
     20     NSMutableData *dstData = [[NSMutableData alloc] initWithData:srcData];
     21     NSUInteger halfLength = byteCount / 2;
     22     for (NSUInteger i=0; i<halfLength; i++) {
     23         NSRange begin = NSMakeRange(i, 1);
     24         NSRange end = NSMakeRange(byteCount - i - 1, 1);
     25         NSData *beginData = [srcData subdataWithRange:begin];
     26         NSData *endData = [srcData subdataWithRange:end];
     27         [dstData replaceBytesInRange:begin withBytes:endData.bytes];
     28         [dstData replaceBytesInRange:end withBytes:beginData.bytes];
     29     }//for
     30     
     31     return dstData;
     32 }
     33 
     34 + (NSData *)byteFromUInt8:(uint8_t)val
     35 {
     36     NSMutableData *valData = [[NSMutableData alloc] init];
     37     
     38     unsigned char valChar[1];
     39     valChar[0] = 0xff & val;
     40     [valData appendBytes:valChar length:1];
     41     
     42     return [self dataWithReverse:valData];
     43 }
     44 
     45 + (NSData *)bytesFromUInt16:(uint16_t)val
     46 {
     47     NSMutableData *valData = [[NSMutableData alloc] init];
     48     
     49     unsigned char valChar[2];
     50     valChar[0] = 0xff & val;
     51     valChar[1] = (0xff00 & val) >> 8;
     52     [valData appendBytes:valChar length:2];
     53     
     54     return [self dataWithReverse:valData];
     55 }
     56 
     57 + (NSData *)bytesFromUInt32:(uint32_t)val
     58 {
     59     NSMutableData *valData = [[NSMutableData alloc] init];
     60     
     61     unsigned char valChar[4];
     62     valChar[0] = 0xff & val;
     63     valChar[1] = (0xff00 & val) >> 8;
     64     valChar[2] = (0xff0000 & val) >> 16;
     65     valChar[3] = (0xff000000 & val) >> 24;
     66     [valData appendBytes:valChar length:4];
     67     
     68     return [self dataWithReverse:valData];
     69 }
     70 
     71 + (NSData *)bytesFromUInt64:(uint64_t)val
     72 {
     73     NSMutableData *valData = [[NSMutableData alloc] init];
     74     
     75     unsigned char valChar[8];
     76     valChar[0] = 0xff & val;
     77     valChar[1] = (0xff00 & val) >> 8;
     78     valChar[2] = (0xff0000 & val) >> 16;
     79     valChar[3] = (0xff000000 & val) >> 24;
     80     valChar[4] = (0xff00000000 & val) >> 32;
     81     valChar[5] = (0xff0000000000 & val) >> 40;
     82     valChar[6] = (0xff000000000000 & val) >> 48;
     83     valChar[7] = (0xff00000000000000 & val) >> 56;
     84     [valData appendBytes:valChar length:8];
     85     
     86     return [self dataWithReverse:valData];
     87 }
     88 
     89 + (NSData *)bytesFromValue:(NSInteger)value byteCount:(int)byteCount
     90 {
     91     NSAssert(value <= 4294967295, @"bytesFromValue: (max value is 4294967295)");
     92     NSAssert(byteCount <= 4, @"bytesFromValue: (byte count is too long)");
     93     
     94     NSMutableData *valData = [[NSMutableData alloc] init];
     95     NSUInteger tempVal = value;
     96     int offset = 0;
     97     
     98     while (offset < byteCount) {
     99         unsigned char valChar = 0xff & tempVal;
    100         [valData appendBytes:&valChar length:1];
    101         tempVal = tempVal >> 8;
    102         offset++;
    103     }//while
    104     
    105     return valData;
    106 }
    107 
    108 + (NSData *)bytesFromValue:(NSInteger)value byteCount:(int)byteCount reverse:(BOOL)reverse
    109 {
    110     NSData *tempData = [self bytesFromValue:value byteCount:byteCount];
    111     if (reverse) {
    112         return tempData;
    113     }
    114     
    115     return [self dataWithReverse:tempData];
    116 }
    117 
    118 + (uint8_t)uint8FromBytes:(NSData *)fData
    119 {
    120     NSAssert(fData.length == 1, @"uint8FromBytes: (data length != 1)");
    121     NSData *data = fData;
    122     uint8_t val = 0;
    123     [data getBytes:&val length:1];
    124     return val;
    125 }
    126 
    127 + (uint16_t)uint16FromBytes:(NSData *)fData
    128 {
    129     NSAssert(fData.length == 2, @"uint16FromBytes: (data length != 2)");
    130     NSData *data = [self dataWithReverse:fData];;
    131     uint16_t val0 = 0;
    132     uint16_t val1 = 0;
    133     [data getBytes:&val0 range:NSMakeRange(0, 1)];
    134     [data getBytes:&val1 range:NSMakeRange(1, 1)];
    135     
    136     uint16_t dstVal = (val0 & 0xff) + ((val1 << 8) & 0xff00);
    137     return dstVal;
    138 }
    139 
    140 + (uint32_t)uint32FromBytes:(NSData *)fData
    141 {
    142     NSAssert(fData.length == 4, @"uint16FromBytes: (data length != 4)");
    143     NSData *data = [self dataWithReverse:fData];
    144     
    145     uint32_t val0 = 0;
    146     uint32_t val1 = 0;
    147     uint32_t val2 = 0;
    148     uint32_t val3 = 0;
    149     [data getBytes:&val0 range:NSMakeRange(0, 1)];
    150     [data getBytes:&val1 range:NSMakeRange(1, 1)];
    151     [data getBytes:&val2 range:NSMakeRange(2, 1)];
    152     [data getBytes:&val3 range:NSMakeRange(3, 1)];
    153     
    154     uint32_t dstVal = (val0 & 0xff) + ((val1 << 8) & 0xff00) + ((val1 << 16) & 0xff0000) + ((val1 << 24) & 0xff000000);
    155     return dstVal;
    156 }
    157 
    158 + (NSInteger)valueFromBytes:(NSData *)data
    159 {
    160     NSAssert(data.length <= 4, @"valueFromBytes: (data is too long)");
    161     
    162     NSUInteger dataLen = data.length;
    163     NSUInteger value = 0;
    164     int offset = 0;
    165     
    166     while (offset < dataLen) {
    167         uint32_t tempVal = 0;
    168         [data getBytes:&tempVal range:NSMakeRange(offset, 1)];
    169         value += (tempVal << (8 * offset));
    170         offset++;
    171     }//while
    172     
    173     return value;
    174 }
    175 
    176 + (NSInteger)valueFromBytes:(NSData *)data reverse:(BOOL)reverse
    177 {
    178     NSData *tempData = data;
    179     if (reverse) {
    180         tempData = [self dataWithReverse:tempData];
    181     }
    182     return [self valueFromBytes:tempData];
    183 }
    184 
    185 + (NSData *)dataFromHexString:(NSString *)hexString
    186 {
    187     NSAssert((hexString.length > 0) && (hexString.length % 2 == 0), @"hexString.length mod 2 != 0");
    188     NSMutableData *data = [[NSMutableData alloc] init];
    189     for (NSUInteger i=0; i<hexString.length; i+=2) {
    190         NSRange tempRange = NSMakeRange(i, 2);
    191         NSString *tempStr = [hexString substringWithRange:tempRange];
    192         NSScanner *scanner = [NSScanner scannerWithString:tempStr];
    193         unsigned int tempIntValue;
    194         [scanner scanHexInt:&tempIntValue];
    195         [data appendBytes:&tempIntValue length:1];
    196     }
    197     return data;
    198 }
    199 
    200 + (NSString *)hexStringFromData:(NSData *)data
    201 {
    202     NSAssert(data.length > 0, @"data.length <= 0");
    203     NSMutableString *hexString = [[NSMutableString alloc] init];
    204     const Byte *bytes = data.bytes;
    205     for (NSUInteger i=0; i<data.length; i++) {
    206         Byte value = bytes[i];
    207         Byte high = (value & 0xf0) >> 4;
    208         Byte low = value & 0xf;
    209         [hexString appendFormat:@"%x%x", high, low];
    210     }//for
    211     return hexString;
    212 }
    213 
    214 + (NSString *)asciiStringFromHexString:(NSString *)hexString
    215 {
    216     NSMutableString *asciiString = [[NSMutableString alloc] init];
    217     const char *bytes = [hexString UTF8String];
    218     for (NSUInteger i=0; i<hexString.length; i++) {
    219         [asciiString appendFormat:@"%0.2X", bytes[i]];
    220     }
    221     return asciiString;
    222 }
    223 
    224 + (NSString *)hexStringFromASCIIString:(NSString *)asciiString
    225 {
    226     NSMutableString *hexString = [[NSMutableString alloc] init];
    227     const char *asciiChars = [asciiString UTF8String];
    228     for (NSUInteger i=0; i<asciiString.length; i+=2) {
    229         char hexChar = '';
    230         
    231         //high
    232         if (asciiChars[i] >= '0' && asciiChars[i] <= '9') {
    233             hexChar = (asciiChars[i] - '0') << 4;
    234         } else if (asciiChars[i] >= 'a' && asciiChars[i] <= 'z') {
    235             hexChar = (asciiChars[i] - 'a' + 10) << 4;
    236         } else if (asciiChars[i] >= 'A' && asciiChars[i] <= 'Z') {
    237             hexChar = (asciiChars[i] - 'A' + 10) << 4;
    238         }//if
    239         
    240         //low
    241         if (asciiChars[i+1] >= '0' && asciiChars[i+1] <= '9') {
    242             hexChar += asciiChars[i+1] - '0';
    243         } else if (asciiChars[i+1] >= 'a' && asciiChars[i+1] <= 'z') {
    244             hexChar += asciiChars[i+1] - 'a' + 10;
    245         } else if (asciiChars[i+1] >= 'A' && asciiChars[i+1] <= 'Z') {
    246             hexChar += asciiChars[i+1] - 'A' + 10;
    247         }//if
    248         
    249         [hexString appendFormat:@"%c", hexChar];
    250     }
    251     return hexString;
    252 }
    253 
    254 @end

    剩下的留给读者自行下载这个Demo看代码吧,链接: http://pan.baidu.com/s/1hsi7tNQ 密码: byiy

    尊重劳动成果,转载请注明出处;iOS开发之Socket通信实战--Request请求数据包编码模块

    另外补充一个发现到的博客:初用 CocoaAsyncSocket

  • 相关阅读:
    第18课 类型萃取(2)_获取返回值类型的traits
    第17课 类型萃取(1)_基本的type_traits
    【ASP.NET MVC系列】浅谈数据注解和验证
    【ASP.NET MVC系列】浅谈NuGet在VS中的运用
    【ASP.NET MVC系列】浅谈ASP.NET MVC 视图
    【ASP.NET MVC系列】浅谈ASP.NET MVC运行过程
    【Java系列】Eclipse与Tomcat
    【java系列】java开发环境搭建
    【设计模式篇】工厂模式
    【架构篇】OCP和依赖注入
  • 原文地址:https://www.cnblogs.com/goodboy-heyang/p/5636470.html
Copyright © 2020-2023  润新知