基于Socket之CocoaAsyncSocket使用

本文介紹了CocoaAsyncSocket庫中GCDAsyncSocket類的使用蜂奸、粘包處理以及時間延遲測試.

一.CocoaAsyncSocket介紹

CocoaAsyncSocket中主要包含兩個類: GCDAsyncSocket针饥、GCDAsyncUdpSocket

1)GCDAsyncSocket

用GCD搭建的基于TCP/IP協(xié)議的socket網(wǎng)絡(luò)庫
GCDAsyncSocket is a TCP/IP socket networking library built atop Grand Central Dispatch. -- 引自CocoaAsyncSocket.

2)GCDAsyncUdpSocket

用GCD搭建的基于UDP/IP協(xié)議的socket網(wǎng)絡(luò)庫.
GCDAsyncUdpSocket is a UDP/IP socket networking library built atop Grand Central Dispatch..-- 引自CocoaAsyncSocket.

二.下載CocoaAsyncSocket

點擊CocoaAsyncSocket下載

Jietu20200407-133555.png

拷貝下面兩個文件到項目
GCDAsyncSocket.h
GCDAsyncSocket.m

三.客戶端介紹

先講解客戶端創(chuàng)建過程井佑,大部分項目已經(jīng)有服務(wù)端socket。

步驟:

1).繼承GCDAsyncSocketDelegate協(xié)議.
2).聲明屬性

// 客戶端socket
@property (strong, nonatomic) GCDAsyncSocket *clientSocket;

3).創(chuàng)建socket并指定代理對象為self,代理隊列必須為主隊列.

self.clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

4).連接指定主機的對應(yīng)端口.

NSError *error = nil;
self.connected = [self.clientSocket connectToHost:self.addressTF.text onPort:[self.portTF.text integerValue] viaInterface:nil withTimeout:-1 error:&error];

5).成功連接主機對應(yīng)端口號.

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port 
{
//    NSLog(@"連接主機對應(yīng)端口%@", sock);
    [self showMessageWithStr:@"鏈接成功"];
    [self showMessageWithStr:[NSString stringWithFormat:@"服務(wù)器IP: %@-------端口: %d", host,port]];

    // 連接成功開啟定時器
    [self addTimer];
    // 連接后,可讀取服務(wù)端的數(shù)據(jù)
    [self.clientSocket readDataWithTimeout:- 1 tag:0];
    self.connected = YES;
}

注意:
The host parameter will be an IP address, not a DNS name.(連接的主機為IP地址,并非DNS名稱.) -- 引自GCDAsyncSocket

6).發(fā)送數(shù)據(jù)給服務(wù)端

// 發(fā)送數(shù)據(jù)
- (IBAction)sendMessageAction:(id)sender
{
    NSData *data = [self.messageTextF.text dataUsingEncoding:NSUTF8StringEncoding];
    // withTimeout -1 : 無窮大,一直等
    // tag : 消息標記
    [self.clientSocket writeData:data withTimeout:- 1 tag:0];
}

注意:
*發(fā)送數(shù)據(jù)主要通過- (void)writeData:(NSData )data withTimeout:(NSTimeInterval)timeout tag:(long)tag寫入數(shù)據(jù)的.

7).讀取服務(wù)端數(shù)據(jù)

/**
 讀取數(shù)據(jù)
 @param sock 客戶端socket
 @param data 讀取到的數(shù)據(jù)
 @param tag 本次讀取的標記
 */
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag 
{
    NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    [self showMessageWithStr:text];
    // 讀取到服務(wù)端數(shù)據(jù)值后,能再次讀取
    [self.clientSocket readDataWithTimeout:- 1 tag:0];
}

注意:
有的人寫好代碼,而且第一次能夠讀取到數(shù)據(jù),之后,再也接收不到數(shù)據(jù).那是因為,在讀取到數(shù)據(jù)的代理方法中,需要再次調(diào)用[self.clientSocket readDataWithTimeout:- 1 tag:0];方法,框架本身就是這么設(shè)計的.

8).客戶端socket斷開連接.

/**
 客戶端socket斷開
 @param sock 客戶端socket
 @param err 錯誤描述
 */
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    [self showMessageWithStr:@"斷開連接"];
    self.clientSocket.delegate = nil;
    self.clientSocket = nil;
    self.connected = NO;
    [self.connectTimer invalidate];
}

注意:
sokect斷開連接時,需要清空代理和客戶端本身的socket.
self.clientSocket.delegate = nil;
self.clientSocket = nil;

9).建立心跳連接.

 // 計時器
@property (nonatomic, strong) NSTimer *connectTimer;

// 添加定時器
- (void)addTimer
{
 // 長連接定時器
self.connectTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self  selector:@selector(longConnectToSocket) userInfo:nil repeats:YES];
     // 把定時器添加到當前運行循環(huán),并且調(diào)為通用模式
    [[NSRunLoop currentRunLoop] addTimer:self.connectTimer forMode:NSRunLoopCommonModes];
}
// 心跳連接
- (void)longConnectToSocket
{
    // 發(fā)送固定格式的數(shù)據(jù),指令@"longConnect"
    float version = [[UIDevice currentDevice] systemVersion].floatValue;
    NSString *longConnect = [NSString stringWithFormat:@"123%f",version];

    NSData  *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding];

    [self.clientSocket writeData:data withTimeout:- 1 tag:0];
}

注意:
心跳連接中發(fā)送給服務(wù)端的數(shù)據(jù)只是作為測試代碼,根據(jù)你們公司需求,或者和后臺商定好心跳包的數(shù)據(jù)以及發(fā)送心跳的時間間隔.因為這個項目的服務(wù)端socket也是我個人寫的,所以,我自定義心跳包協(xié)議.客戶端發(fā)送心跳包,服務(wù)端也需要有對應(yīng)的心跳檢測,以此檢測客戶端是否在線.

代理方法如下:
/socket成功連接到才會服務(wù)器調(diào)用
  -(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
 //接受到新的socket連接才會調(diào)用
  - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket;
 //讀取數(shù)據(jù),有數(shù)據(jù)就會調(diào)用
  - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
 //直到讀到這個長度的數(shù)據(jù),才會觸發(fā)代理
  - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 
 //直到讀到data這個邊界位衩,才會觸發(fā)代理 
  - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
 //有socket斷開連接調(diào)用
  - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err;

四.模擬服務(wù)端

1.繼承GCDAsyncSocketDelegate協(xié)議.
2.聲明屬性
3.創(chuàng)建socket并指定代理對象為self,代理隊列必須為主隊列.
4.開放服務(wù)端的指定端口.
5.連接上新的客戶端socket
6.發(fā)送數(shù)據(jù)給客戶端
7.讀取客戶端的數(shù)據(jù)
8.建立檢測心跳連接.

心跳檢測方法只提供部分思路:

1.懶加載一個可變字典,字典的鍵作為客戶端的標識.如:客戶端標識為123456.

*2.在- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData )data withTag:(long)tag方法中,將讀取到的數(shù)據(jù)或者數(shù)據(jù)中的部分字符串作為鍵.字典的值為系統(tǒng)當前時間.服務(wù)端第一次讀取數(shù)據(jù)時,字典中沒有數(shù)據(jù),所以,直接添加到可變字典中,之后每次讀取數(shù)據(jù)時,都用字典的setObject: forKey:方法添加字典,若存儲的鍵相同,即客戶端標識相同,鍵會被覆蓋,再使用系統(tǒng)的當前時間作為值.

3.在- (void)checkLongConnect中,獲取此時的當前時間,遍歷字典,將每個鍵的值和當前時間進行比較即可.判斷的延遲時間可以寫8秒.時間自定.之后,再根據(jù)自己的需求進行后續(xù)處理.

五.數(shù)據(jù)粘包處理

粘包解決思路:

思路1)
發(fā)送方將數(shù)據(jù)包加上包頭和包尾,包頭冀续、包體以及包尾用字典形式包裝成json字符串,接收方,通過解析獲取json字符串中的包體,便可進行進一步處理.

思路2)
添加前綴.和包內(nèi)容拼接成同一個字符串.

思路3)
如果最終要得到的數(shù)據(jù)的長度是個固定長度,用一個字符串作為緩沖池,每次收到數(shù)據(jù),都用字符串拼接對應(yīng)數(shù)據(jù),每當字符串的長度和固定長度相同時,便得到一個完整數(shù)據(jù),處理完這個數(shù)據(jù)并清空字符串,再進行下一輪的字符拼接.

六.檢驗測試.

1)測試配置

測試時,兩端需要處于同一WiFi下.客戶端中的IP地址為服務(wù)端的IP地址,具體信息進入Wifi設(shè)置中查看.

2)測試所需環(huán)境.

將客戶端程序安裝在每個客戶端,讓一臺服務(wù)端測試機和一臺客戶端測試機連接mac并運行,這兩臺測試機可以看到打印結(jié)果,所有由服務(wù)端發(fā)送到客戶端的數(shù)據(jù),通過客戶端再回傳給服務(wù)端,在服務(wù)端看打印結(jié)果.

3)進行延遲差測試.

延遲差即服務(wù)端發(fā)送數(shù)據(jù)到第一臺客戶端和服務(wù)端發(fā)送數(shù)據(jù)到最后一臺客戶端的時間差.根據(jù)服務(wù)端發(fā)送數(shù)據(jù)給不同數(shù)量的客戶端進行測試.而且,發(fā)送數(shù)據(jù)時,是隨機發(fā)送.

4)單次信息收發(fā)測試.

讓服務(wù)端給每個客戶端隨機發(fā)送200次數(shù)據(jù).并計算服務(wù)端發(fā)送數(shù)據(jù)到某一客戶端,完整的一次收發(fā)時間情況.

學習資料查詢?nèi)缦拢?/p>

https://www.cnblogs.com/letougaozao/p/3842113.html

http://www.cocoachina.com/ios/20170615/19529.html

https://www.cnblogs.com/XYQ-208910/p/5169209.html

IOS 詳解socket編程[oc]粘包琼讽、半包處理

iOS開發(fā)項目- 基于WebSocket的聊天通訊(2)

iOS開發(fā)項目- 基于WebSocket的聊天通訊(1)

IOS開發(fā)網(wǎng)絡(luò)篇—Socket編程詳解

iOS App通信之local socket示例

iOS App之間的通信 local socket

IOS中使用 CocoaAsyncSocket?

iOS 使用 socket 實現(xiàn)即時通信示例(非第三方庫)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洪唐,隨后出現(xiàn)的幾起案子跨琳,更是在濱河造成了極大的恐慌,老刑警劉巖桐罕,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脉让,死亡現(xiàn)場離奇詭異,居然都是意外死亡功炮,警方通過查閱死者的電腦和手機溅潜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薪伏,“玉大人滚澜,你說我怎么就攤上這事〖藁常” “怎么了设捐?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長塘淑。 經(jīng)常有香客問我萝招,道長,這世上最難降的妖魔是什么存捺? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任槐沼,我火速辦了婚禮,結(jié)果婚禮上捌治,老公的妹妹穿的比我還像新娘岗钩。我一直安慰自己,他們只是感情好肖油,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布兼吓。 她就那樣靜靜地躺著,像睡著了一般森枪。 火紅的嫁衣襯著肌膚如雪视搏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天疲恢,我揣著相機與錄音凶朗,去河邊找鬼。 笑死显拳,一個胖子當著我的面吹牛棚愤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宛畦,長吁一口氣:“原來是場噩夢啊……” “哼瘸洛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起次和,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤反肋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后踏施,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體石蔗,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年畅形,在試婚紗的時候發(fā)現(xiàn)自己被綠了养距。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡日熬,死狀恐怖棍厌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竖席,我是刑警寧澤耘纱,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站毕荐,受9級特大地震影響束析,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜东跪,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一畸陡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧虽填,春花似錦、人聲如沸曹动。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墓陈。三九已至恶守,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贡必,已是汗流浹背兔港。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仔拟,地道東北人衫樊。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親科侈。 傳聞我的和親對象是個殘疾皇子载佳,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容