iOS IM開發準備工作(四)CocoaAsyncSocket的使用

来源:http://www.cnblogs.com/akforsure/archive/2016/03/02/5235559.html
-Advertisement-
Play Games

上一篇亂說了一陣socket,這篇要說說怎麼幹活了。畢竟用過的起來才行。 我的項目裡面使用的是CocoaAsyncSocket,這個是對CFSocket的封裝。如果你覺得自己可以實現封裝或者直接用原生的,我可以告訴你,很累;關鍵是等你弄出來,項目可能都要交了。這個庫,支持TCP和UDP;有GCD和R


  上一篇亂說了一陣socket,這篇要說說怎麼幹活了。畢竟用的起來才行。

  我的項目裡面使用的是CocoaAsyncSocket,這個是對CFSocket的封裝。如果你覺得自己可以實現封裝或者直接用原生的,我可以告訴你,很累;關鍵是等你弄出來,項目可能都要交了。這個庫,支持TCP和UDP;有GCD和RunLoop兩種選擇。UDP相比TCP的話,可靠性低一點,一般用來傳輸視頻,少個一兩幀沒有什麼影響。這裡我就說一下TCP的使用,當然為了發揮Apple設備的牛X性能,我用GCD。

    

建立socket 單例

  先說一點,要保持長連接。我們需要建一個全局的單例。標準單例模式寫法⬇️

1 + (instancetype)ShareBaseClient {
2     static SocketClient * iSocketCilent;
3     static dispatch_once_t onceToken;
4     dispatch_once(&onceToken, ^{
5         iSocketCilent = [[SocketClient alloc]init];
6     });
7     return iSocketCilent;
8 }
 1 #define USE_SECURE_CONNECTION    0 // 是否需要使用安全連接
 2 #define USE_CFSTREAM_FOR_TLS     0 // Use old-school CFStream style technique
 3 #define MANUALLY_EVALUATE_TRUST  1 // 是否需要人工驗證
 4 
 5 
 6 - (id)init {
 7     self = [super init];
 8     // Start the socket stuff 最後的那個是你的要放在哪個線程裡面操作 果斷全局
 9     asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
10     NSError *error = nil;
11     uint16_t port = DefalutPORT;// 預設的埠號
12     // 連接是否成功 
13     // WWW_HOST 連接的地址
14     // 50秒 是我設置的 超時時間
15     if (![asyncSocket connectToHost:WWW_HOST onPort:port withTimeout:50.0f error:&error]){
16         
17         NSLog(@"Unable to connect to due to invalid configuration: %@", error);
18 
19     }else{
20         NSLog(@"Connecting to \"%@\" on port %hu...", WWW_HOST, port);
21     }
22     {
23 #if USE_SECURE_CONNECTION
24         
25 #if USE_CFSTREAM_FOR_TLS
26         {
27             // Use old-school CFStream style technique
28             
29             NSDictionary *options = @{
30                                       GCDAsyncSocketUseCFStreamForTLS : @(YES),
31                                       GCDAsyncSocketSSLPeerName : CERT_HOST
32                                       };
33             
34             DDLogVerbose(@"Requesting StartTLS with options:\n%@", options);
35             [asyncSocket startTLS:options];
36         }
37 #elif MANUALLY_EVALUATE_TRUST
38         {
39             // Use socket:didReceiveTrust:completionHandler: delegate method for manual trust evaluation
40             
41             NSDictionary *options = @{
42                                       GCDAsyncSocketManuallyEvaluateTrust : @(YES),
43                                       GCDAsyncSocketSSLPeerName : CERT_HOST
44                                       };
45             
46             DDLogVerbose(@"Requesting StartTLS with options:\n%@", options);
47             [asyncSocket startTLS:options];
48         }
49 #else
50         {
51             // Use default trust evaluation, and provide basic security parameters
52             
53             NSDictionary *options = @{
54                                       GCDAsyncSocketSSLPeerName : CERT_HOST
55                                       };
56             
57             DDLogVerbose(@"Requesting StartTLS with options:\n%@", options);
58             [asyncSocket startTLS:options];
59         }
60 #endif
61 #endif
62     }
63     return self;
64 }

 

接受報文 

  如果你連接成功了,那你就可以開始收發報文了。我這裡用的是delegate模式,所以我們要實現一下幾個比較重要的代理:

  連接上的第一步,我們要準備讀數據。我用一個簡單的枚舉標識了每次讀取數據的區域,一般報文都是先讀報頭,再去解析正文的。所以連接上之後,要首先接受報頭。

 1 #define READ_HEADER_LINE_BY_LINE  0
 2 
 3 typedef NS_ENUM(long, ReadTagType){ //讀取數據的類型
 4     ReadTagTypehead = 1,// 報頭
 5     ReadTagTypebody = 2 // 主體
 6 };
 7 
 8 - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
 9 {
10     NSLog(@"socket:didConnectToHost:%@ port:%hu", host, port);
11 
12     // 是否一行一行的讀數據,我這裡設置的是 0
13 #if READ_HEADER_LINE_BY_LINE
14     // Now we tell the socket to read the first line of the http response header.
15     // As per the http protocol, we know each header line is terminated with a CRLF (carriage return, line feed).
16     [asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1.0 tag:0];
17     
18 #else
19     // Now we tell the socket to read the full header for the http response.
20     // As per the http protocol, we know the header is terminated with two CRLF's (carriage return, line feed).
21     // sizeof(protocol_head)  一個報文頭的長度
22     // ReadTagTypehead 先讀的是報文的頭的
23     // -1代表沒有超時時間
24     [asyncSocket readDataToLength:sizeof(protocol_head) withTimeout:-1 tag:ReadTagTypehead];
25     
26 #endif
27 }

  開始讀取後,有數據的話會進入這個代理。

 1 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
 2     
 3     switch (tag) {
 4         case ReadTagTypehead:// 先 讀取 head部分
 5         {
 6              // 拿到之後,我們處理一下這段頭
 7              // 獲得head裡面的命令CMD 和 下一段正文的長度BodyLength
 8              // 把這些參數傳到一個方法裡面處理一下(萬一是空頭呢。)
 9             
10             [self willReadBody:CMD size: BodyLength data:data];
11         }
12             break;
13         case ReadTagTypebody:// 再 讀取 body部分
14         {
15             // 加上這一段,我們取得了完整的數據了,可以給需要的地方發過去了
16             [self haveReadData:data];
17             // 然後我們去讀下一個報文,還是先讀報文頭
18             [asyncSocket readDataToLength:sizeof(protocol_head) withTimeout:-1 tag:ReadTagTypehead];
19         }
20             break;
21         default:
22             break;
23     }
24 }
25 
26 // 處理用的方法 下麵兩個是我寫的私有方法 不是CocoaAsyncSocket的代理方法
27 // 準備讀取body的數據 CMD需要記錄下來的
28 - (void)willReadBody:(int)CMD size:(long)size data:(NSData *)data{
29    gCMD = CMD;
30     alldata = [[NSMutableData alloc]initWithData: data];
31     if(size == 0) {// 如果是空頭,就丟掉這段數據,繼續讀下一個頭
32         [self haveReadData:nil];
33         [asyncSocket readDataToLength:sizeof(protocol_head) withTimeout:-1 tag:ReadTagTypehead];
34     }else {// 如果不是的話,就去讀下一段 報文的正文
35         [asyncSocket readDataToLength:dataBufferSize withTimeout:-1 tag:ReadTagTypebody];
36     }
37 }
38 
39 // 讀取完數據了 要回調代理
40 - (void)haveReadData:(NSData *)data {
41     // 把頭和正文拼接起來 構成完整的數據
42     if(data && data.length>0)
43         [alldata appendData:data];
44     
45 //    NSLog(@"receiveData: %@",alldata);
46     if(alldata.length>0) {
47        // 這裡去告訴 你需要處理數據的地方 讓他去處理數據
48     }
49     alldata = nil;
50 }

  到上面這一步的話,你的接受數據就算完成了。

發送報文

  [asyncSocket writeData:data withTimeout:-1.0 tag:0]; 

  就一行,不用懷疑。把你的信息轉為NSData後 調用這個方法就好了。成功的話,會進入這個代理。

  - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag; 

 

socket 斷開

  當然,socket也有斷開的時候,所以你需要處理這個代理。這裡面你可以發個通知,讓你的application斷線重連一下。PS:移動網路和Wi-Fi切換,socket會斷開哦。

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err

 

附加:

  當我們連續收到錯誤報文的時候,我們需要主動斷開socket。

// 先去掉代理 再斷開連接
self.asyncSocket.delegate = nil;
[self.asyncSocket disconnect];

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第二章:商場促銷——策略模式 策略模式的定義: 策略模式是一種定義一系列演算法的方法,從概念上來看,所有這些演算法完成的都是相同的工作,知識實現不同,他可以以相同的方式調用所有的演算法,減少了各類演算法類與使用演算法類之間的耦合 策略模式的優點 : 1. 策略模式的Strategy 類層次為Context定義
  • Atitit.biz業務系統 面向框架 面向模式---------數據映射imp 1.1. 面向變數 面向過程 面向對象 面向組件 面向框架 面向服務 面向模式1 1.2. 第2章 架構模式 18 1 1.3. 第3章 設計模式 143 2 1.4. 面向對象中的面向變數全局變數問題2 1.5. 面
  • Atitit.wrmi web rmi框架新特性 1. V1d 新特性1 1.1. 增加了精確參數1 1.2. 增加了req參數,命名參數模式。。1 1.3. 增加了globale 傳遞隱含參數req resp等1 1.4. Cs bs兩個版本的實現1 2. V2 新特性2 2.1. $req對象預
  • 協議 協議只有方法的聲明(類似於其他編程語言的介面) 協議相當於大家都所遵循的 關鍵字 @protocol 協議名 <所遵循的協議> 預設NSObject @end @protocollamcoProtocol <NSObject>@required //必須實現的方法 -(void)study;@
  • 本軟體設定用戶第一個接觸到的功能就是頁面載入等待功能,這個功能對使用者來說就是一個持續1、2秒鐘的等待頁面,在用戶等待的同時程式做一些必要的檢查以及數據準備工作,載入頁面分為UI篇和功能篇,從表及里首先是UI的實現,一個軟體除功能之外還得有一個光鮮的外表也是非常重要的,儘管本人設計水平一般但是還是親
  • 先看下onBackPressed和onKeyDown的區別 在Android上有兩種方法來獲取該按鈕的事件 1.直接獲取按鈕按下事件,此方法相容Android 1.0到Android 2.1 也是常規方法,直接重寫Activity的onKeyDown方法即可,代碼如下: @Override publ
  • 帶你走進游戲開發的世界之游戲幀動畫的處理<ignore_js_op> 1.幀動畫的原理 幀動畫幀動畫顧名思義,一幀一幀播放的動畫就是幀動畫。 幀動畫和我們小時候看的動畫片的原理是一樣的,在相同區域快速切換圖片給人們呈現一種視覺的假象感覺像是在播放動畫,其實不過是N張圖片在一幀一幀的切換罷了。 如圖所
  • 很多時候,AFNetworking都是目前iOS開發者網路庫中的不二選擇。Github上2W+的star數足見其流行程度。而從iOS7.0開始,蘋果推出了新的網路庫繼承者NSURLSession後,AFNetworking也毫不猶豫地加入了對其的支持。3.0+更加只是提供了NSURLSession的
一周排行
    -Advertisement-
    Play Games
  • ## 引言 最近發現自己喜歡用的 Todo 軟體總是差點意思,畢竟每個人的習慣和工作流不太一樣,我就想著自己寫一個小的[Todo 項目]( https://github.com/circler3/TodoTrack ),核心的功能是自動記錄 Todo 執行過程中消耗的時間(尤其面向程式員),按照自己 ...
  • ### 前言 當我們編寫 C# 代碼時,經常需要處理大量的數據集合。在傳統的方式中,我們往往需要先將整個數據集合載入到記憶體中,然後再進行操作。但是如果數據集合非常大,這種方式就會導致記憶體占用過高,甚至可能導致程式崩潰。 C# 中的`yield return`機制可以幫助我們解決這個問題。通過使用`y ...
  • 1. ADO.NET的前世今生 ADO.NET的名稱起源於ADO(ActiveX Data Objects),是一個COM組件庫,用於在以往的Microsoft技術中訪問數據。之所以使用ADO.NET名稱,是因為Microsoft希望表明,這是在NET編程環境中優先使用的數據訪問介面。 ADO.NE ...
  • 1. 為什麼需要單元測試 在我們之前,測試某些功能是否能夠正常運行時,我們都將代碼寫到Main方法中,當我們測試第二個功能時,我們只能選擇將之前的代碼清掉,重新編寫。此時,如果你還想重新測試你之前的功能時,這時你就顯得有些難為情了,因為代碼都被你清掉了。當然你完全可以把代碼寫到一個記事本中進行記錄, ...
  • 1. 透過現象看本質 反射被譽為是 c#中的黑科技 ,在很多領域中都有反射的身影,例如,我們經常使用的ORM框架,ABP框架 等。 反射指程式可以訪問、檢測和修改它本身狀態或行為的一種能力。. 程式集包含模塊,而模塊包含類型,類型又包含成員。. 反射則提供了封裝程式集、模塊和類型的對象。. 您可以使 ...
  • # Rust Web 全棧開發之 Web Service 中的錯誤處理 ## Web Service 中的統一錯誤處理 ### Actix Web Service 自定義錯誤類型 -> 自定義錯誤轉為 HTTP Response - 資料庫 - 資料庫錯誤 - 串列化 - serde 錯誤 - I/ ...
  • 在前面的幾篇文章中,詳細地給大家介紹了Java里的集合。但在介紹集合時,我們涉及到了泛型的概念卻並沒有詳細學習,所以今天我們要花點時間給大家專門講解什麼是泛型、泛型的作用、用法、特點等內容 ...
  • ###BIO:同步阻塞 主線程發起io請求後,需要等待當前io操作完成,才能繼續執行。 ###NIO:同步非阻塞 引入selector、channel、等概念,當主線程發起io請求後,輪詢的查看系統是否準備好執行io操作,沒有準備好則主線程不會阻塞會繼續執行,準備好主線程會阻塞等待io操作完成。 # ...
  • 摘要:在讀多寫少的環境中,有沒有一種比ReadWriteLock更快的鎖呢?有,那就是JDK1.8中新增的StampedLock! 本文分享自華為雲社區《【高併發】高併發場景下一種比讀寫鎖更快的鎖》,作者: 冰 河。 什麼是StampedLock? ReadWriteLock鎖允許多個線程同時讀取共 ...
  • ## 併發與並行😣 ### 併發與並行的概念和區別 並行:同一個時間段內多個任務同時在不同的CPU核心上執行。強調同一時刻多個任務之間的”**同時執行**“。 併發:同一個時間段內多個任務都在進展。強調多個任務間的”**交替執行**“。 ![](https://img2023.cnblogs.co ...