这篇文章,我们介绍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__); }
查看本文章之前,可以看看