iOS WebSocket长链接的实现方法
WebSocket
WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但是它和HTTP最大不同是:WebSocket是一种双向通信协议.
由于项目需要创建一个聊天室,需要通过长链接,和后台保持通讯,进行聊天,并且实时进行热点消息的推送.
目前Facebook的SocketRocket应该是目前最好的关于SocketRocket使用的框架了.而且简单易用.
使用
一般一个项目在启动后的某个时机会启动创建一个长链接,如果需要多个就多次创建.如果只要一个就可以封装为一个单例,全局使用.
可以使用podpod管理库,在podfile中加入
pod'SocketRocket'
在使用命令行工具cd到当前工程安装
podinstall
导入头文件后即可使用.
为求稳定,我的做法是copy的FaceBook里SocketRocket库到项目里.-->SocketRocket地址
1.首先创建一个名为WebSocketManager的单例类,
+(instancetype)shared;
2.创建一个枚举,分别表示WebSocket的链接状态
typedefNS_ENUM(NSUInteger,WebSocketConnectType){ WebSocketDefault=0,//初始状态,未连接,不需要重新连接 WebSocketConnect,//已连接 WebSocketDisconnect//连接后断开,需要重新连接 };
3.创建连接
//建立长连接 -(void)connectServer;
4.处理连接成功的结果;
-(void)webSocketDidOpen:(RMWebSocket*)webSocket;//连接成功回调
5.处理连接失败的结果
-(void)webSocket:(SRWebSocket*)webSocketdidFailWithError:(NSError*)error;//连接失败回调
6.接收消息
///接收消息回调,需要提前和后台约定好消息格式. -(void)webSocket:(SRWebSocket*)webSocketdidReceiveMessageWithString:(nonnullNSString*)string
7.关闭连接
-(void)webSocket:(SRWebSocket*)webSocketdidCloseWithCode:(NSInteger)codereason:(NSString*)reasonwasClean:(BOOL)wasClean;///关闭连接回调的代理
8.为保持长链接的连接状态,需要定时向后台发送消息,就是俗称的:心跳包.
需要创建一个定时器,固定时间发送消息.
9.链接断开情况处理:
首先判断是否是主动断开,如果是主动断开就不作处理.
如果不是主动断开链接,需要做重新连接的逻辑.
具体代码如下:
WebSocketManager.h中的代码
#import#import"RMWebSocket.h" typedefNS_ENUM(NSUInteger,WebSocketConnectType){ WebSocketDefault=0,//初始状态,未连接 WebSocketConnect,//已连接 WebSocketDisconnect//连接后断开 }; @classWebSocketManager; @protocolWebSocketManagerDelegate -(void)webSocketManagerDidReceiveMessageWithString:(NSString*)string; @end NS_ASSUME_NONNULL_BEGIN @interfaceWebSocketManager:NSObject @property(nonatomic,strong)RMWebSocket*webSocket; @property(nonatomic,weak)id delegate; @property(nonatomic,assign)BOOLisConnect;//是否连接 @property(nonatomic,assign)WebSocketConnectTypeconnectType; +(instancetype)shared; -(void)connectServer;//建立长连接 -(void)reConnectServer;//重新连接 -(void)RMWebSocketClose;//关闭长连接 -(void)sendDataToServer:(NSString*)data;//发送数据给服务器 @end NS_ASSUME_NONNULL_END
WebSocketManager.m中的代码
#import"WebSocketManager.h" @interfaceWebSocketManager()@property(nonatomic,strong)NSTimer*heartBeatTimer;//心跳定时器 @property(nonatomic,strong)NSTimer*netWorkTestingTimer;//没有网络的时候检测网络定时器 @property(nonatomic,assign)NSTimeIntervalreConnectTime;//重连时间 @property(nonatomic,strong)NSMutableArray*sendDataArray;//存储要发送给服务端的数据 @property(nonatomic,assign)BOOLisActivelyClose;//用于判断是否主动关闭长连接,如果是主动断开连接,连接失败的代理中,就不用执行重新连接方法 @end @implementationWebSocketManager +(instancetype)shared{ staticWebSocketManager*_instance=nil; staticdispatch_once_tonceToken; dispatch_once(&onceToken,^{ _instance=[[selfalloc]init]; }); return_instance; } -(instancetype)init { self=[superinit]; if(self){ self.reConnectTime=0; self.isActivelyClose=NO; self.sendDataArray=[[NSMutableArrayalloc]init]; } returnself; } //建立长连接 -(void)connectServer{ self.isActivelyClose=NO; self.webSocket.delegate=nil; [self.webSocketclose]; _webSocket=nil; //self.webSocket=[[RMWebSocketalloc]initWithURL:[NSURLURLWithString:@"https://dev-im-gateway.runxsports.com/ws/token=88888888"]]; self.webSocket=[[RMWebSocketalloc]initWithURL:[NSURLURLWithString:@"ws://chat.workerman.net:7272"]]; self.webSocket.delegate=self; [self.webSocketopen]; } -(void)sendPing:(id)sender{ [self.webSocketsendPing:nilerror:NULL]; } #pragmamark-------------------------------------------------- #pragmamark-socketdelegate ///开始连接 -(void)webSocketDidOpen:(RMWebSocket*)webSocket{ NSLog(@"socket开始连接"); self.isConnect=YES; self.connectType=WebSocketConnect; [selfinitHeartBeat];///开始心跳 } ///连接失败 -(void)webSocket:(RMWebSocket*)webSocketdidFailWithError:(NSError*)error{ NSLog(@"连接失败"); self.isConnect=NO; self.connectType=WebSocketDisconnect; DLog(@"连接失败,这里可以实现掉线自动重连,要注意以下几点"); DLog(@"1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连"); DLog(@"3.连接次数限制,如果连接失败了,重试10次左右就可以了"); //判断网络环境 if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus==AFNetworkReachabilityStatusNotReachable){//没有网络 [selfnoNetWorkStartTestingTimer];//开启网络检测定时器 }else{//有网络 [selfreConnectServer];//连接失败就重连 } } ///接收消息 -(void)webSocket:(RMWebSocket*)webSocketdidReceiveMessageWithString:(NSString*)string{ NSLog(@"接收消息----%@",string); if([self.delegaterespondsToSelector:@selector(webSocketManagerDidReceiveMessageWithString:)]){ [self.delegatewebSocketManagerDidReceiveMessageWithString:string]; } } ///关闭连接 -(void)webSocket:(RMWebSocket*)webSocketdidCloseWithCode:(NSInteger)codereason:(NSString*)reasonwasClean:(BOOL)wasClean{ self.isConnect=NO; if(self.isActivelyClose){ self.connectType=WebSocketDefault; return; }else{ self.connectType=WebSocketDisconnect; } DLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",code,reason,wasClean); [selfdestoryHeartBeat];//断开连接时销毁心跳 //判断网络环境 if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus==AFNetworkReachabilityStatusNotReachable){//没有网络 [selfnoNetWorkStartTestingTimer];//开启网络检测 }else{//有网络 NSLog(@"关闭连接"); _webSocket=nil; [selfreConnectServer];//连接失败就重连 } } ///ping -(void)webSocket:(RMWebSocket*)webSocketdidReceivePong:(NSData*)pongData{ NSLog(@"接受pong数据-->%@",pongData); } #pragmamark-NSTimer //初始化心跳 -(void)initHeartBeat{ //心跳没有被关闭 if(self.heartBeatTimer){ return; } [selfdestoryHeartBeat]; dispatch_main_async_safe(^{ self.heartBeatTimer=[NSTimertimerWithTimeInterval:10target:selfselector:@selector(senderheartBeat)userInfo:nilrepeats:true]; [[NSRunLoopcurrentRunLoop]addTimer:self.heartBeatTimerforMode:NSRunLoopCommonModes]; }) } //重新连接 -(void)reConnectServer{ if(self.webSocket.readyState==RM_OPEN){ return; } if(self.reConnectTime>1024){//重连10次2^10=1024 self.reConnectTime=0; return; } WS(weakSelf); dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(self.reConnectTime*NSEC_PER_SEC)),dispatch_get_main_queue(),^{ if(weakSelf.webSocket.readyState==RM_OPEN&&weakSelf.webSocket.readyState==RM_CONNECTING){ return; } [weakSelfconnectServer]; //CTHLog(@"正在重连......"); if(weakSelf.reConnectTime==0){//重连时间2的指数级增长 weakSelf.reConnectTime=2; }else{ weakSelf.reConnectTime*=2; } }); } //发送心跳 -(void)senderheartBeat{ //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小 WS(weakSelf); dispatch_main_async_safe(^{ if(weakSelf.webSocket.readyState==RM_OPEN){ [weakSelfsendPing:nil]; } }); } //没有网络的时候开始定时--用于网络检测 -(void)noNetWorkStartTestingTimer{ WS(weakSelf); dispatch_main_async_safe(^{ weakSelf.netWorkTestingTimer=[NSTimerscheduledTimerWithTimeInterval:1.0target:weakSelfselector:@selector(noNetWorkStartTesting)userInfo:nilrepeats:YES]; [[NSRunLoopcurrentRunLoop]addTimer:weakSelf.netWorkTestingTimerforMode:NSDefaultRunLoopMode]; }); } //定时检测网络 -(void)noNetWorkStartTesting{ //有网络 if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus!=AFNetworkReachabilityStatusNotReachable) { //关闭网络检测定时器 [selfdestoryNetWorkStartTesting]; //开始重连 [selfreConnectServer]; } } //取消网络检测 -(void)destoryNetWorkStartTesting{ WS(weakSelf); dispatch_main_async_safe(^{ if(weakSelf.netWorkTestingTimer) { [weakSelf.netWorkTestingTimerinvalidate]; weakSelf.netWorkTestingTimer=nil; } }); } //取消心跳 -(void)destoryHeartBeat{ WS(weakSelf); dispatch_main_async_safe(^{ if(weakSelf.heartBeatTimer) { [weakSelf.heartBeatTimerinvalidate]; weakSelf.heartBeatTimer=nil; } }); } //关闭长连接 -(void)RMWebSocketClose{ self.isActivelyClose=YES; self.isConnect=NO; self.connectType=WebSocketDefault; if(self.webSocket) { [self.webSocketclose]; _webSocket=nil; } //关闭心跳定时器 [selfdestoryHeartBeat]; //关闭网络检测定时器 [selfdestoryNetWorkStartTesting]; } //发送数据给服务器 -(void)sendDataToServer:(NSString*)data{ [self.sendDataArrayaddObject:data]; //[_webSocketsendString:dataerror:NULL]; //没有网络 if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus==AFNetworkReachabilityStatusNotReachable) { //开启网络检测定时器 [selfnoNetWorkStartTestingTimer]; } else//有网络 { if(self.webSocket!=nil) { //只有长连接OPEN开启状态才能调send方法,不然会Crash if(self.webSocket.readyState==RM_OPEN) { //if(self.sendDataArray.count>0) //{ //NSString*data=self.sendDataArray[0]; [_webSocketsendString:dataerror:NULL];//发送数据 //[self.sendDataArrayremoveObjectAtIndex:0]; // //} } elseif(self.webSocket.readyState==RM_CONNECTING)//正在连接 { DLog(@"正在连接中,重连后会去自动同步数据"); } elseif(self.webSocket.readyState==RM_CLOSING||self.webSocket.readyState==RM_CLOSED)//断开连接 { //调用reConnectServer方法重连,连接成功后继续发送数据 [selfreConnectServer]; } } else { [selfconnectServer];//连接服务器 } } } @end
注意点
我们在发送消息之前,也就是调用 senderheartBeat/sendDataToServer:方法之前,一定要判断当前scoket是否连接,如果不是连接状态,程序则会crash。
iOS手机屏幕息屏或者回主页的时候有可能会造成链接断开,我这边的处理是在回到屏幕的时候,判断状态,如果已经断开,就重新连接.
在AppDelegate中:
-(void)applicationDidBecomeActive:(UIApplication*)application{ //Restartanytasksthatwerepaused(ornotyetstarted)whiletheapplicationwasinactive.Iftheapplicationwaspreviouslyinthebackground,optionallyrefreshtheuserinterface. if([WebSocketManagershared].connectType==WebSocketDisconnect){ [[WebSocketManagershared]connectServer]; } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。