標籤:
這篇文章,我們介紹CocoaAsyncSocket架構的使用,主要介紹實現用戶端/伺服器端代碼,相信在網上已經很多這樣的文章了,這裡做一下自己的總結。這裡介紹使用GCD方式
一.用戶端
1.
讀者可以在github下載架構源碼 https://github.com/robbiehanson/CocoaAsyncSocket
下載後,可以看到在Examples下面可以看到很多例子,如果讀者自學能力高,可以略過下面的文章。
2.開始使用
1)在 \Source\GCD 目錄下,我們可以看到GCDAsyncSocket的h與m檔案,我們複製這兩個檔案到項目中。
2)我們在代碼裡面匯入標頭檔 #import "GCDAsyncSocket.h" 與 協議 GCDAsyncSocketDelegate。
3.代碼
1)定義全域變數 GCDAsyncSocket *_socket;
2)開始串連伺服器
- (void) connect2Server() { //1.主機與連接埠號碼 NSString *host = @"127.0.0.1"; int port = 12345; //初始化socket,這裡有兩種方式。分別為是主/子線程中運行socket。根據項目不同而定 _socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];//這種是在主線程中運行 //_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; 這種是在子線程中運行 //開始串連 NSError *error = nil; [_socket connectToHost:host onPort port error:&error]; if(error) { NSLog("error:%@", error); } }
3)登入
-(void)login() { //登入String NSString *loginStr = "iam:I am login!"; NSData *loginData = [loginStr dataUsingEncoding: NSUTF8StringEncoding]; //發送登入指令。-1表示不逾時。tag200表示這個指令的標識,很大用處 [_socket writeData: loginData withTimeout:-1 tag:200];}
4)開始聊天
//發送聊天資料-(void) sendMsg: (NSString*)msg{ NSString *sendMsg = [@"msg:I send message to u!"]; NSData *sendData = [sendMsg dataUsingEncoding: NSUTF8StringEncoding]; [_socket writeData; sendData withTimeout:-1 tag:201];}
5)實現協議delegate
#pragma mark -socket的代理#pragma mark 串連成功-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{ //串連成功 NSLog(@"%s",__func__);}#pragma mark 中斷連線-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{ if (err) { NSLog(@"串連失敗"); }else{ NSLog(@"正常斷開"); }}#pragma mark 資料發送成功-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{ NSLog(@"%s",__func__); //發送完資料手動讀取 [sock readDataWithTimeout:-1 tag:tag];}#pragma mark 讀取資料-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ //代理是在主/子線程調用 NSLog(@"%@",[NSThread currentThread]); NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if (tag == 200) { //登入指令 }else if(tag == 201){ //聊天資料 } NSLog(@"%s %@",__func__,receiverStr);}
二.伺服器端
伺服器端不可能在iphone/ipad上運行,所以我們建立一個單純命令列的項目。
1.建立一個類MyChatServer,並且開放公用方法startServer,標頭檔如下
#import <Foundation/Foundation.h>@interface MyChatServer : NSObject/** * 開啟聊天伺服器 */-(void)startServer;@end
2.在main檔案中,開啟聊天伺服器,並且開啟主運行迴圈,讓伺服器接收到用戶端請求
//實現聊天MyChatServer *chatServer = [[MyChatServer alloc] init];[chatServer startServer];//開啟主運行迴圈[[NSRunLoop currentRunLoop] run];
3.在MychatServer.m檔案中,我們開始實現GCDAsyncSocket的方法(下面代碼都在MychatServer.m中)
1)首先建一個成員變數array對象,把所有的socket(用戶端)對象儲存在裡面
@property(strong,nonatomic)NSMutableArray *clientSocket;
私人變數serverSocket與golbal_queue
GCDAsyncSocket *_serverSocket;dispatch_queue_t _golbalQueue;
2)在類初始化的時候,我們初始化私人變數與成員變數
-(instancetype)init{ if (self = [super init]) { _clientSocket = [NSMutableArray array]; //建立全域queue _golbalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //建立服務端的socket,注意這裡的是初始化的同時已經指定了delegate _serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_golbalQueue]; } return self;}
3)開啟伺服器聊天startServer
-(void)startChatServer{ //開啟監聽連接埠
NSError *err; [_serverSocket acceptOnPort:12345 error:&err]; if (!err) { NSLog(@"服務開啟成功"); }else{ NSLog(@"服務開啟失敗"); }}
4)實現delegate
#pragma mark 有用戶端建立串連的時候調用-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{ //sock為服務端的socket,服務端的socket只負責用戶端的串連,不負責資料的讀取。 newSocket為用戶端的socket
NSLog(@"服務端的socket %p 用戶端的socket %p",sock,newSocket); //儲存用戶端的socket,如果不儲存,伺服器會自動斷開與用戶端的串連(用戶端那邊會報中斷連線的log) NSLog(@"%s",__func__); [self.clientSocket addObject:newSocket]; //newSocket為用戶端的Socket。這裡讀取資料 [newSocket readDataWithTimeout:-1 tag:100];}#pragma mark 伺服器寫資料給用戶端-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{ NSLog(@"%s",__func__); [sock readDataWithTimeout:-1 tag:100];}#pragma mark 接收用戶端傳遞過來的資料-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ //sock為用戶端的socket NSLog(@"用戶端的socket %p",sock); //接收到資料 NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"length:%ld",receiverStr.length); // 把斷行符號和換行字元去掉,接收到的字串有時候包括這2個,導致判斷quit指令的時候判斷不相等 receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\r" withString:@""]; receiverStr = [receiverStr stringByReplacingOccurrencesOfString:@"\n" withString:@""]; //判斷是登入指令還是發送聊天資料的指令。這些指令都是自訂的 //登入指令 if([receiverStr hasPrefix:@"iam:"]){
// 擷取使用者名稱 NSString *user = [receiverStr componentsSeparatedByString:@":"][1]; // 響應給用戶端的資料 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 [self.clientSocket removeObject:sock]; } NSLog(@"%s",__func__);}
查看本文章之前,可以看看
IOS Socket 03-建立串連與登入
IOS Socket 04-利用架構CocoaAsyncSocket實現用戶端/伺服器端