這篇文章,我們介紹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__); }
查看本文章之前,可以看看
可以關注本人的公眾號,多年經驗的原創文章共享給大家。