IOS应用的长连接如果保证效率的话,一般在应用内使用自己的socket长连接,在退出应用或者应用切换到后台之后使用苹果的推送来通知,这样就保证了在软件使用过程中,如果苹果服务器出问题不至于影响软件使用的情况。
一、IOS的长连接库
这个demo使用了一个第三方库:CocoaAsyncSocket,通过这个库来实现长连接
CocoaAsyncSocket下载
Github下载地址:https://github.com/DamonHu/CocoaAsyncSocket
GitOsc下载地址:http://git.oschina.net/DamonHoo/CocoaAsyncSocket
demo下载
Github下载地址:https://github.com/DamonHu/AsyncSocketDemo
GitOsc下载地址:http://git.oschina.net/DamonHoo/AsyncSocketDemo
二、长连接的流程
Socket长连接分为TCP和UDP两种,区别就是握手验证的区别,包括TCP的三次握手这个都可以百度下,连接的流程就是请求连接,连接成功之后客户端向服务器发送数据,服务器把处理的数据发送给客户端。
和HTTP请求流程差不多,区别就是http请求每次更新数据都要向对应的端口发送一次请求,之后返回数据之后关闭连接,而长连接就是客户端和服务器一直连着,当有数据更新的时候,服务器会直接发给客户端,不需要客户端主动请求。
在这过程中,为了保证服务端和客户端一直是连接状态,客户端会定时不间断的发送心跳数据到服务器,表明还连接着,不然长时间没有数据更新,会断开连接,这样一直有心跳数据的时候,就会保证了连接没有中断,至于心跳数据的内容,就是前端后端共同商量的,和请求的数据是单独的。就相当于单独出来一个请求,数据是商量之后的心跳数据。
三、代码实现
UDP和TCP在这个库代码没什么区别,所以用TCP示例
流程就是
发送连接请求
请求成功开始定时发送心跳数据
向服务器发送数据
服务器向客户端发送数据,客户端改变
断网重连
用户关闭长连接
1、发送链接请求
创建一个tcp长连接,并且可以设定tcp内容,这个是内容是自己用来区分不同的socket请求的,然后通过connectToHost可以发送连接请求
-(void)tcpTest:(UIButton*)sender { NSLog(@"tcpTest"); self.myTcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; [self tcpConnetwithData:[NSString stringWithFormat:@"%d",(int)sender.tag]]; } -(void)tcpConnetwithData:(NSString*)userData { [self.myTcpSocket setUserData:userData]; NSError *error = nil; if (![ self.myTcpSocket connectToHost:@"ddceo.com" onPort:80 withTimeout:2.0f error:&error]) { NSLog(@"error:%@",error); } }
2、请求成功开始定时发送心跳数据
发送完连接请求之后,如果连接成功会有成功回调,表示连接成功,在成功之后,可以开始发送心跳数据了
//连接 - (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port { NSLog(@"didConnectToHost"); //连上之后可以每隔30s发送一次心跳包 self.mytime =[NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(heartbeatFunc) userInfo:nil repeats:YES]; [self.mytime fire]; }
3、向服务器发送数据
发送数据用的就是这个函数
[self.myTcpSocket writeData:data withTimeout:10.0f tag:0];
所以心跳数据和正常数据的请求区别就是一个内容是用户传的,一个是前后端规定的
//发送心跳包 -(void)heartbeatFunc { //心跳包的内容是前后端自定义的 NSString *heart = @"Damon"; NSData *data= [heart dataUsingEncoding:NSUTF8StringEncoding]; [self.myTcpSocket writeData:data withTimeout:10.0f tag:0]; } //发送数据 -(void)sendData { NSString *dataStr = @"Damon_Hu"; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; [self.myTcpSocket writeData:data withTimeout:10.0f tag:1]; NSString *dataStr2 = @"Damon_Hu2"; NSData *data2 = [dataStr2 dataUsingEncoding:NSUTF8StringEncoding]; [self.myTcpSocket writeData:data2 withTimeout:10.0f tag:2]; }
向服务器发送数据完成之后,客户端也会有一个回调函数
//向服务器发送完数据之后回调 - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag { NSLog(@"have send"); if (tag == 1) { NSLog(@"first send"); } else if (tag ==2){ NSLog(@"second send"); } }
在这个回调函数可以做相关逻辑。
4、服务器向客户端发送数据,客户端更新相关界面
当服务器向客户端发送有数据的时候,会调用这个函数,然后做对应的逻辑
//本地接收到数据之后回调 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { [self.myTcpSocket readDataWithTimeout:10.0f tag:tag]; //接受到数据之后写入本地 [self reciveData:data]; } //接受数据更新本地内容 -(void)reciveData:(NSData*)data { //接收到的数据写入本地 NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]); }
5、断网重连和手动断网
当心跳数据断了的时候,需要判断是用户手动切断的,还是因为网络原因断了,所以需要自己在前端做一个判断是哪个原因。
首先定义一个状态值
enum{ SOCKET_OFFLINE_SERVER = -2,//服务器断开 SOCKET_OFFLINE_USER, //用户主动断开 };
如果是用户手动切断的,那么就修改状态值,断开链接和停止心跳包发送
-(void)disConnectSocket { for (GCDAsyncSocket *socket in self.mySocketArray) { socket.userData = [NSString stringWithFormat:@"%d",SOCKET_OFFLINE_USER]; [self.mytime invalidate]; //停止心跳包发送 [socket disconnect]; //断开链接 } }
如果不是用户手动切断,那就是其他原因了,所以需要断网重连接,断开连接的时候,会有这个回调
//断开连接 - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err { NSLog(@"socketDidDisconnect"); //主动断开 if ([sock.userData isEqualToString:[NSString stringWithFormat:@"%d",SOCKET_OFFLINE_USER]]) { return; } else{ NSLog(@"%@",err); //断线重连 [self tcpConnetwithData:@"1"]; } }
需要做的就是判断如果是用户主动断开就不再重连接,如果不是主动断开的,那么就重新请求连接即可。
四、后续说明
这个是关于Socket链接的demo,但是有很多地方需要调试,因为没有服务端,所以暂时这样,到有服务端的时候,再调试下,其他具体未说细节可以百度,这个demo就是说明一个思路,我服务端还没有搭建,所以没有测试,后面测试通过之后补充上来,demo用了masonry库自动布局,使用的pod导入。
五、参考文章:
版权属于:东哥笔记 - DongGe.me
本文链接:https://dongge.org/blog/351.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!