iOS開發之Socket通訊實戰--Request請求資料包編碼模組,ios--request

來源:互聯網
上載者:User

iOS開發之Socket通訊實戰--Request請求資料包編碼模組,ios--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語言的基礎資料型別 (Elementary Data Type),但是在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     }//while104     105     return valData;106 }107 108 + (NSData *)bytesFromValue:(NSInteger)value byteCount:(int)byteCount reverse:(BOOL)reverse109 {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 *)fData119 {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 *)fData128 {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 *)fData141 {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 *)data159 {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     }//while172     173     return value;174 }175 176 + (NSInteger)valueFromBytes:(NSData *)data reverse:(BOOL)reverse177 {178     NSData *tempData = data;179     if (reverse) {180         tempData = [self dataWithReverse:tempData];181     }182     return [self valueFromBytes:tempData];183 }184 185 + (NSData *)dataFromHexString:(NSString *)hexString186 {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 *)data201 {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     }//for211     return hexString;212 }213 214 + (NSString *)asciiStringFromHexString:(NSString *)hexString215 {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 *)asciiString225 {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 = '\0';230         231         //high232         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         }//if239         240         //low241         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         }//if248         249         [hexString appendFormat:@"%c", hexChar];250     }251     return hexString;252 }253 254 @end

剩下的留給讀者自行下載這個Demo看代碼吧,連結: http://pan.baidu.com/s/1hsi7tNQ 密碼: byiy

 

尊重勞動成果,轉載請註明出處;iOS開發之Socket通訊實戰--Request請求資料包編碼模組

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.