// // UCWebSocketSever.m // wolfman // // Created by 张灿 on 2017/6/19. // Copyright © 2017年 shareSmile. All rights reserved. // #import "UCWebSocketSever.h" #import "UCSocketParmaModel.h" #import "SRSecurityPolicy.h" //通话已经取消(自己退出) @interface UCWebSocketSever() @property(nonatomic,strong)SRWebSocket* webSocket; @property(nonatomic,strong) dispatch_queue_t socketQueue; @property(nonatomic,copy)reconnectBlock reconnectblock; @property(nonatomic,assign)NSTimeInterval timeout; @property(nonatomic,strong)dispatch_source_t heartTimer; @property(nonatomic,assign)NSInteger reCount; //重连40次 @end @implementation UCWebSocketSever +(instancetype)sharedWebSocketServer{ static UCWebSocketSever* shareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shareInstance = [[super allocWithZone:NULL]init]; shareInstance.socketQueue = dispatch_queue_create("com.caicaicp.webSocket", nil); shareInstance.reCount = 0; }); return shareInstance; } + (instancetype)allocWithZone:(struct _NSZone *)zone{ return [UCWebSocketSever sharedWebSocketServer]; } - (id)copyWithZone:(struct _NSZone *)zone { return [UCWebSocketSever sharedWebSocketServer]; } - (void)startConnentWebSocket:(NSInteger)reconn { if (!self.webSocket) { NSString* urlString; urlString = [NSString stringWithFormat:@"%@&reconnect=%zd",self.serveUrl,reconn]; NSLog(@"webSocket网址--%@",urlString); self.webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] securityPolicy:[SRSecurityPolicy defaultPolicy]]; self.webSocket.delegate = self; [self.webSocket open]; } } - (void)removeDelegate{ self.delegate = nil; } - (void)closeConnectWebSocket{ [self.webSocket close]; self.webSocket.delegate = nil; self.webSocket = nil; [self.timer invalidate]; // 将定时器从运行循环中移除, self.timer = nil; if (self.heartTimer) { dispatch_source_cancel(self.heartTimer);//删除GCD定时器 self.heartTimer = nil; } } - (void)resetConnect{ self.reCount++; [self closeConnectWebSocket]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self startConnentWebSocket:1]; }); } //连接成功回调 - (void)webSocketDidOpen:(SRWebSocket *)webSocket { NSLog(@"连接成功"); self.reCount = 0; NSLog(@"连接成功,可以立刻登录你公司后台的服务器了,还有开启心跳"); if (!self.timer) { self.timer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(sendHeartMsg:) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode]; } if (!self.heartTimer) { [self timePaddingStart]; } if (self.reconnectblock) { self.reconnectblock(); self.reconnectblock = nil; } } //连接失败回调 - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { NSLog(@"连接失败,这里可以实现掉线自动重连,要注意以下几点"); NSLog(@"1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连"); NSLog(@"2.判断调用层是否需要连接,例如用户都没在聊天界面,连接上去浪费流量"); //add by leo 弱网断开警告 if (self.delegate&& [self.delegate respondsToSelector:@selector(weakNetWorkWaring)]) { // [self.delegate weakNetWorkWaring]; } // if (self.reCount<40) { [self resetConnect]; // } } //关闭连接回调的代理 - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { // if (code==1001) { //服务器断开 自动重连 NSLog(@"服务器断开连接--code%zd--reson%@",code,reason); if (self.webSocket) { // if (self.reCount<40) { [self resetConnect]; // } } // }else if(code==1005){//客户端自己手动断开 // // } } //接收消息 - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { // NSLog(@"接收消息"); if ([message isKindOfClass:[NSString class]]) { NSString* str = (NSString*)message; // NSLog(@"STR=%@",str); NSArray* array = [str componentsSeparatedByString:@"@@"]; for (NSString* tempStr in array) { if (![tempStr isEqualToString:@""]) { UCPacketBody *body = [[UCPacketBody alloc] initWithData:[tempStr dataUsingEncoding:NSUTF8StringEncoding]]; if (self.delegate&& [self.delegate respondsToSelector:@selector(didRecvMessage:)]) { [self.delegate didRecvMessage:body]; } NSDictionary* dict = [body.data mj_JSONObject]; UCSocketParmaModel* param = [UCSocketParmaModel mj_objectWithKeyValues:dict]; if ([param.method isEqualToString:@"heartbeat"]) { //重置时间 self.timeout = 40; } } } } } - (void)sendData:(NSData*)data{ @weakify(self); dispatch_async(self.socketQueue, ^{ @strongify(self); if (self.webSocket != nil) { // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩 if (self.webSocket.readyState == SR_OPEN) { [self.webSocket sendData:data error:nil]; // 发送数据 } else if (self.webSocket.readyState == SR_CONNECTING) { NSLog(@"正在连接中,重连后其他方法会去自动同步数据"); // 每隔2秒检测一次 socket.readyState 状态,检测 10 次左右 // 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据 // 如果 10 次都还是没连上的,那这个发送请求就丢失了,这种情况是服务器的问题了,小概率的 } else if (self.webSocket.readyState == SR_CLOSING || self.webSocket.readyState == SR_CLOSED) { // websocket 断开了,调用 reConnect 方法重连 [self reConnect:^{ @strongify(self); [self.webSocket sendData:data error:nil]; }]; } } else { NSLog(@"webSocket已被清空"); } }); } //重新发送 - (void)reConnect:(reconnectBlock)block{ [self resetConnect]; self.reconnectblock = block; } //发送心跳消息 - (void)sendHeartMsg:(NSTimer*) theTimer { //发送心跳消息 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:@"heartbeat" forKey:@"method"]; [dict setObject:@{@"room_id":self.roomId} forKey:@"data"]; NSData* data = [dict mj_JSONData]; if( [self.webSocket sendData:data error:nil]){ NSLog(@"发送了心跳消息N"); } } //收到心跳回复的倒计时 - (void)timePaddingStart{ self.timeout = 40; @weakify(self); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue); self.heartTimer = timer; dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行 dispatch_source_set_event_handler(timer, ^{ @strongify(self); if(self.timeout<=0){ //没有收到心跳回调 重连if (self.webSocket.readyState!=SR_OPEN) { NSLog(@"没有收到回调,断开重连"); // if (self.reCount<40) { [self resetConnect]; // } }else{ self.timeout--; } }); dispatch_resume(timer); } @end