CocoaAsyncSocket小記

之前都沒有做過相關(guān)socket的柬焕,閑著就找了些相關(guān)的socket荐吵,發(fā)現(xiàn)自己寫的還是比較繁瑣的混卵。默默的找了下第三方CocoaAsyncSocket拙泽。雖然網(wǎng)上有很多關(guān)于CocoaAsyncSocket的帖子,但是還是得自己實(shí)踐还蹲,自己寫一篇來(lái)的記憶深刻爹耗,理解更深入。下面開始干貨谜喊,demo源碼潭兽。
由于沒有服務(wù)器,只好自己默默的搭一下端對(duì)端的斗遏,其中一個(gè)當(dāng)做服務(wù)器山卦,另一個(gè)當(dāng)然就是用戶端了。

服務(wù)端

導(dǎo)入CocoaAsyncSocket诵次,就不說(shuō)了账蓉,本文講的是使用GCD版本的TCP即時(shí)通訊,非UDP逾一。
導(dǎo)入頭文件#import "GCDAsyncSocket.h"铸本,并遵循代理GCDAsyncSocketDelegate
創(chuàng)建對(duì)應(yīng)的控件

//手動(dòng)設(shè)置端口號(hào)輸入框
@property (weak, nonatomic) IBOutlet UITextField *portField;

@property (weak, nonatomic) IBOutlet UITextField *ipField;
//服務(wù)端給用戶端發(fā)送消息輸入框
@property (weak, nonatomic) IBOutlet UITextField *msgField;
//開始監(jiān)聽按鈕
@property (weak, nonatomic) IBOutlet UIButton *beginBtn;
//發(fā)送消息按鈕
@property (weak, nonatomic) IBOutlet UIButton *senderBtn;
//接收消息按鈕
@property (weak, nonatomic) IBOutlet UIButton *receiveBtn;
//接收到消息顯示文本
@property (weak, nonatomic) IBOutlet UITextView *receiveLabel;
//服務(wù)器socket(開放端口,監(jiān)聽客戶端socket的鏈接)
@property(nonatomic, strong) GCDAsyncSocket *serverSocket;
//保護(hù)客戶端socket
@property (strong, nonatomic) GCDAsyncSocket *clientSocket;
初始化服務(wù)端Socket
//初始化服務(wù)器socket,在主線程回調(diào)
    self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
實(shí)現(xiàn)GCDAsyncSocketDelegate

代理 didAcceptNewSocket遵堵,接收對(duì)方發(fā)送過來(lái)的socket箱玷,獲取到對(duì)應(yīng)的消息

- (void)socket:(GCDAsyncSocket*)sock didAcceptNewSocket:(GCDAsyncSocket*)newSocket{
    
    //sock為服務(wù)端的socket怨规,服務(wù)端的socket只負(fù)責(zé)客戶端的連接,不負(fù)責(zé)數(shù)據(jù)的讀取锡足。   newSocket為客戶端的socket
    
    //保存客戶端的socket
    _clientSocket = newSocket;
    
    NSLog(@"服務(wù)端的socket %p 客戶端的socket %p",sock,newSocket);

    [self showMessageWithStr:@"鏈接成功"];
    
    [self showMessageWithStr:[NSString stringWithFormat:@"服務(wù)器地址:%@ -端口:%d", newSocket.connectedHost, newSocket.connectedPort]];
    
    [newSocket readDataWithTimeout:-1 tag:0];//超時(shí)波丰,以及標(biāo)記tag
    
}

代理didWriteDataWithTag,服務(wù)器寫數(shù)據(jù)給客戶端

//服務(wù)器寫數(shù)據(jù)給客戶端
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    NSLog(@"%s",__func__);
    [sock readDataWithTimeout:-1 tag:100];
}

代理didReadData舶得,收到消息

//收到消息
- (void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag{
    //sock為客戶端的socket
    //接收到數(shù)據(jù)
    NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    [self showMessageWithStr:receiverStr];
    
//    // 把回車和換行字符去掉掰烟,接收到的字符串有時(shí)候包括這2個(gè),導(dǎo)致判斷quit指令的時(shí)候判斷不相等
//    receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\r" withString:@""];
//    receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    /*
    //判斷是登錄指令還是發(fā)送聊天數(shù)據(jù)的指令沐批。這些指令都是自定義的
    //登錄指令
    if([receiverStr hasPrefix:@"iam:"]){
        // 獲取用戶名
        NSString *user = [receiverStr componentsSeparatedByString:@":"][1];
        // 響應(yīng)給客戶端的數(shù)據(jù)
        NSString *respStr = [user stringByAppendingString:@"has joined"];
        [sock writeData:[respStr dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    }
    //聊天指令
    if ([receiverStr hasPrefix:@"msg:"]) {
        //截取聊天消息
        NSString *msg = [receiverStr componentsSeparatedByString:@":"][1];
        [sock writeData:[msg dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
    }
    //quit指令
    if ([receiverStr isEqualToString:@"quit"]) {
        //斷開連接
        [sock disconnect];
        //移除socket
        _clientSocket = nil;
    }
    */
    NSLog(@"%s",__func__);
}
顯示消息
- (void)showMessageWithStr:(NSString *)str
{
    NSString * tmpStr = _receiveLabel.text;
    
    tmpStr = [tmpStr stringByAppendingString:[NSString stringWithFormat:@"\n%@",str]];
    
    [_receiveLabel setText:tmpStr];
}
按鈕Action

開始監(jiān)聽按鈕Action

- (void)beginBtnClick
{
    //2纫骑、開放端口
    NSError*error =nil;
    BOOL result = [self.serverSocket acceptOnPort:self.portField.text.integerValue error:&error];
    if(result && error ==nil) {
        [self showMessageWithStr:@"system:服務(wù)器開啟成功"];
    }
    else [self showMessageWithStr:@"system:服務(wù)器開啟失敗"];
}

發(fā)送消息按鈕Action

- (void)senderBtnClick
{
    NSData *data = [self.msgField.text dataUsingEncoding:NSUTF8StringEncoding];
    
    //tag:消息標(biāo)記,withTimeout -1:無(wú)窮大九孩,一直等
    [_clientSocket writeData:data withTimeout:-1 tag:0];
}

接收消息按鈕Action

- (void)receiveBtnClick
{
    [self.clientSocket readDataWithTimeout:11 tag:0];
}

代碼寫完了惧磺,來(lái)看下界面:

服務(wù)端.png

客戶端

一樣導(dǎo)入頭文件#import "GCDAsyncSocket.h",并遵循代理GCDAsyncSocketDelegate
創(chuàng)建對(duì)應(yīng)的控件

//端口號(hào)輸入框捻撑,對(duì)應(yīng)服務(wù)端的端口號(hào)
@property (weak, nonatomic) IBOutlet UITextField *portField;
//ip地址輸入框磨隘,客戶端的主機(jī)ip地址(這個(gè)需要注意一下因?yàn)橛玫氖荰CP,不是UDP顾患,需要將兩個(gè)端安裝在同一個(gè)設(shè)備上番捂,不然沒法接收消息,因?yàn)閕p地址可以直接用回環(huán)地址:127.0.0.1)
@property (weak, nonatomic) IBOutlet UITextField *ipField;
//消息輸入送框
@property (weak, nonatomic) IBOutlet UITextField *msgField;
//接收消息文本
@property (weak, nonatomic) IBOutlet UILabel *receiveLabel;

@property(nonatomic, strong) GCDAsyncSocket *clientSocket;
初始化
self.clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];//直接全局隊(duì)列
實(shí)現(xiàn)GCDAsyncSocketDelegate

代理didConnectToHost

//連接成功
- (void)socket:(GCDAsyncSocket*)sock didConnectToHost:(NSString*)host port:(uint16_t)port{
    
    [self showMessageWithStr:@"system:連接成功"];
    
    NSLog(@"system:連接成功");

    [self.clientSocket readDataWithTimeout:-1 tag:0];
}

代理didWriteDataWithTag江解,發(fā)送消息

//數(shù)據(jù)發(fā)送成功
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
    NSLog(@"%s",__func__);
    [self showMessageWithStr:@"system:發(fā)送成功"];
    //發(fā)送完數(shù)據(jù)手動(dòng)讀取
    [sock readDataWithTimeout:-1 tag:tag];
}

代理didReadData设预,接收消息

//收到消息
- (void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag{
    
    NSString*text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
    
    [self showMessageWithStr:text];
    
    [self.clientSocket readDataWithTimeout:-1 tag:0];
}

代理socketDidDisconnect,用于監(jiān)聽連接情況

-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{
    if (err) {
        [self showMessageWithStr:@"system:連接失敗"];
        NSLog(@"system:連接失敗");
    }else{
        [self showMessageWithStr:@"system:正常斷開"];
        NSLog(@"system:正常斷開");
    }
}
按鈕Action

開始連接

- (IBAction)beginBtnClickEvent:(id)sender {
 
//    [self.clientSocket connectToHost:self.ipField.text onPort:self.portField.text.intValue withTimeout:-1 error:&error];
    
    [self.clientSocket connectToHost:self.ipField.text onPort:self.portField.text.integerValue viaInterface:nil withTimeout:-1 error:nil];

    NSLog(@"ip:%@,端口:%@",self.ipField.text,self.portField.text);
//
//    NSString *loginStr = @"iam:I am login!";
//    
//    NSData *loginData = [loginStr dataUsingEncoding: NSUTF8StringEncoding];
//    
//    [_clientSocket writeData:loginData withTimeout:-1 tag:0];
}
展示消息
- (void)showMessageWithStr:(NSString *)str
{
    NSLog(@"%@",str);
    self.receiveLabel.text = [self.receiveLabel.text stringByAppendingFormat:@"%@\n", str];

}

下面展示下界面效果犁河,連接對(duì)應(yīng)的客戶端鳖枕,以及用戶端效果

服務(wù)端.png
客戶端.png

就這樣,簡(jiǎn)單的實(shí)現(xiàn)了桨螺,端對(duì)端的連接宾符,以及發(fā)送消息。
GCDAsyncSocketDelegate的代理方法還有好多灭翔,具體的后面還會(huì)研究并且更新文章魏烫,當(dāng)然有興趣的搬磚們可以自己去研究,本文只是一個(gè)簡(jiǎn)單的入門肝箱,如有問題可以給我留言哄褒。
總結(jié)一下,并強(qiáng)調(diào)一下本文用的是GCD版的TCP連接煌张,因?yàn)楸救耸菍⒖蛻舳四派摹⒎?wù)端分開寫在兩個(gè)App上(可以直接寫在同一個(gè)App上,用tabBarController來(lái)實(shí)現(xiàn)一頁(yè)客戶端骏融,一頁(yè)用戶端)链嘀,會(huì)存在一個(gè)問題井辜,那就是你如果安裝在兩個(gè)設(shè)備上你是無(wú)法連接成功的、無(wú)法連接成功管闷、無(wú)法連接成功,需要安裝在同一個(gè)設(shè)備上一開始我就被這個(gè)問題給坑了一下窃肠。當(dāng)然UDP不存在這個(gè)問題包个,以及你客戶端、服務(wù)端寫在同一個(gè)App上也是不存在的無(wú)法連接成功的問題冤留。好了碧囊,要說(shuō)的就這些,碰到的坑代碼里面也有說(shuō)了纤怒,總結(jié)也強(qiáng)調(diào)了糯而。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泊窘,隨后出現(xiàn)的幾起案子熄驼,更是在濱河造成了極大的恐慌,老刑警劉巖烘豹,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓜贾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡携悯,警方通過查閱死者的電腦和手機(jī)祭芦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)憔鬼,“玉大人龟劲,你說(shuō)我怎么就攤上這事≈峄颍” “怎么了昌跌?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)照雁。 經(jīng)常有香客問我避矢,道長(zhǎng),這世上最難降的妖魔是什么囊榜? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任审胸,我火速辦了婚禮,結(jié)果婚禮上卸勺,老公的妹妹穿的比我還像新娘砂沛。我一直安慰自己,他們只是感情好曙求,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布碍庵。 她就那樣靜靜地躺著映企,像睡著了一般。 火紅的嫁衣襯著肌膚如雪静浴。 梳的紋絲不亂的頭發(fā)上堰氓,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音苹享,去河邊找鬼双絮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛得问,可吹牛的內(nèi)容都是我干的囤攀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宫纬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼焚挠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起漓骚,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蝌衔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蝌蹂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胚委,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年叉信,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亩冬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡硼身,死狀恐怖硅急,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佳遂,我是刑警寧澤营袜,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站丑罪,受9級(jí)特大地震影響荚板,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吩屹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一跪另、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧煤搜,春花似錦免绿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)淌哟。三九已至,卻和暖如春辽故,著一層夾襖步出監(jiān)牢的瞬間徒仓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工誊垢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留掉弛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓彤枢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親筒饰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缴啡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20170615/19529.html 參考:h...
    F麥子閱讀 4,011評(píng)論 3 2
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)瓷们,斷路器业栅,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 即使嘗試背離烏云 可卻騙不了水的精靈 雨 無(wú)情地拍打 云海后的太陽(yáng) 你在哪
    KennethWyle閱讀 212評(píng)論 0 4
  • 我們都知道,海內(nèi)過了端午之后谬晕,氣候就逐步最先躁動(dòng)起來(lái)碘裕,恨不得一天都呆在室內(nèi)不想出門,不外更偏向于呆在涼快的處所攒钳。那...
    東莞康泰旅游閱讀 108評(píng)論 0 0
  • 親們,喜歡吃花生嗎焕檬? 我是非常喜歡吃的姆坚,從小就愛,記得小時(shí)候媽媽會(huì)在我們自家自留地上種上一小塊花生实愚,到秋天...
    紅巖168閱讀 413評(píng)論 0 1