// // GWebSocket.m // TUIContact // // Created by gan on 2025/3/24. // #import #import "GWebSocket.h" #import #import "GDBManager.h" #import "ChatListStore.h" #import "ChatsStore.h" #import "WebRTCStore.h" #import "UserNetApi.h" #import "LoginStateManager.h" static int const kHeartbeatDuration = 10; static int const timeoutError = 5; static NSString *ping = @"{\"code\":0}"; static NSString *redeady = @"{\"code\":1}"; @interface GWebSocket () @property (nonatomic,strong) SRWebSocket *socket; @property (strong, nonatomic) NSTimer *heatBeat; @property (nonatomic,strong) NSString *serverIpString; @property (nonatomic,strong) NSString *userid; @property (nonatomic,strong) NSString *UUID; @end @implementation GWebSocket { BOOL _isReachable; } + (GWebSocket *_Nonnull)shareInstance{ static id gShareInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ gShareInstance = [[self alloc] init]; }); return gShareInstance; } - (instancetype)init { if (self = [super init]) { NSUUID *uid = [NSUUID UUID]; _UUID=uid.UUIDString; NSLog(@"_UUID:%@",_UUID); [self startNetworkReachability]; [self startHeartbeat]; [self addObserver]; } return self; } #pragma mark - Public - - (void)connectWebSocket { [self initWebSocket]; } - (void)closeWebSocket { // _UUID=nil; [self close]; } - (void)sendMsg:(NSString *)msg { if (self.socket && self.socket.readyState == SR_OPEN) { // 只有在socket状态为SR_OPEN 时,才可以发送消息 // 在socket状态不为SR_OPEN,可以将消息放进队列里,在websocket连上时,再发送 NSLog(@"sendMsg:%@",msg); [self.socket sendString:msg error:nil]; } else{ NSLog(@"socket--------不可用"); [self reConnectSocket]; } // NSLog(@"_UUID:%@",_UUID); } #pragma mark -- WebSocket //初始化 WebSocket - (void)initWebSocket{ NSDictionary *userinfo =[UDManager.shareInstance getDDManager:dkuserinfo]; self.userid =userinfo[@"id"]; NSString *token = (NSString *)[UDManager.shareInstance getSDManager:gkeytoken]; if(_UUID==nil){ NSUUID *uid = [NSUUID UUID]; _UUID=uid.UUIDString; } NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@token=%@&client=mobile&uuid=%@",WebSocketUrl,token,_UUID]]; //请求 //NSLog(@"url:%@",url); NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; //初始化请求` _socket = [[SRWebSocket alloc] initWithURLRequest:request]; //代理协议` _socket.delegate = self; //直接连接 [_socket open]; } -(void)sendReady{ NSLog(@"------%@",redeady); [self.socket sendString:redeady error:nil]; } -(void)sendPing{ [self.socket sendString:ping error:nil]; } #pragma mark - Heart Timer - - (void)addObserver { // 进入后台 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil]; // 回到前台 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; } - (void)appDidEnterBackground { [self destoryHeartbeat]; } - (void)appWillEnterForeground { [self startHeartbeat]; [self checkReconnectIfNeed]; } //保活机制 探测包 - (void)startHeartbeat { self.heatBeat = [NSTimer scheduledTimerWithTimeInterval:kHeartbeatDuration target:self selector:@selector(heartbeatAction) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.heatBeat forMode:NSRunLoopCommonModes]; } //断开连接时销毁心跳 - (void)destoryHeartbeat{ [_heatBeat invalidate]; _heatBeat = nil; } // 发送心跳 - (void)heartbeatAction { NSLog(@"XTWebSocket heartbeatAction readyState: %ld", self.socket.readyState); BOOL isLoggedIn = [LoginStateManager sharedManager].isLoggedIn; if (!isLoggedIn) { NSLog(@"XTWebSocket heartbeatAction not login!"); return; } switch (self.socket.readyState) { case SR_CONNECTING: break; case SR_OPEN: [self sendPing]; break; case SR_CLOSING: break; case SR_CLOSED: [self reConnectSocket]; break; } } //重连机制 - (void)reConnectSocket{ [self closeWebSocket]; [self initWebSocket]; } // 关闭Socket - (void)close { [self destoryHeartbeat]; [self.socket close]; self.socket = nil; } #pragma mark -- SRWebSocketDelegate //收到服务器消息是回调 - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{ NSLog(@"----XTWebSocket didReceiveMessage:%@",message); if ([message isKindOfClass:[NSString class]]) { NSString *msg = (NSString *)message; NSError *error; if([msg isEqual:ping]){ return; } // NSLog(@"didReceiveMessage:%@",msg); NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; if (error) { NSLog(@"JSON解析错误: %@", error); } else { // NSLog(@"解析后的字典: %@", jsonDict); NSString *code =jsonDict[@"code"]; if([code isEqualToString:SendCode_MESSAGE]){//收到消息数据 [self onmessage:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_READ]){//已读消息 [self onRead:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_OTHER_LOGIN]){//账号在其他地方登陆 NSDictionary *mmsg = jsonDict[@"message"]; NSString *uuid =mmsg[@"uuid"]; if([uuid isEqualToString:_UUID]){ return; } else{ [self logoutAct]; } return; } if([code isEqualToString:SendCode_NEW_FRIEND]){//好友申请 NSLog(@"好友申请----------"); [[NSNotificationCenter defaultCenter] postNotificationName: nkonAddFriendNote object:nil]; return; } if([code isEqualToString:SendCode_GROUP_VALIDATE]){//群申请验证 [[NSNotificationCenter defaultCenter] postNotificationName: nkonEXGroupdNote object:nil]; return; } if([code isEqualToString:SendCode_WEBRTC_result]){//音视频通话结果 [self onmessage:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_WEBRTC_CALL]){//音视频通话请求 [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_WEBRTC_CLOSE]){//关闭音视频通话 [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_WEBRTC_BUSY]){//音视频通话忙碌 [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_WEBRTC_xinling]){//webrtc信令交互 [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_deletemsg]){//删除消息 return; } if([code isEqualToString:SendCode_WEBRTC_DFBUSY]){//对方正在忙 [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]]; return; } if([code isEqualToString:SendCode_RECMsg]){//收到消息回执 return; } } } } //连接成功 - (void)webSocketDidOpen:(SRWebSocket *)webSocket{ NSLog(@"-----XTWebSocket DidOpen: %ld", webSocket.readyState); switch (webSocket.readyState) { case SR_CONNECTING: break; case SR_OPEN:{ [self sendReady]; if (![_heatBeat isValid]) { [self destoryHeartbeat]; [self startHeartbeat]; } }break; case SR_CLOSING: break; case SR_CLOSED: break; } if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) { [self.delegate onSocketConnectionStateChanged:webSocket.readyState]; } } //连接失败的回调 - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{ NSLog(@"XTWebSocket didFailWithError %@",error); // 1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连 // 2.判断调用层是否需要连接,例如用户都没在聊天界面,连接上去浪费流量 if (self.isReachable) { [self reConnectSocket]; } if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) { [self.delegate onSocketConnectionStateChanged:webSocket.readyState]; } } //连接断开的回调 - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{ NSLog(@"XTWebSocket Close code %ld reason %@",(long)code,reason); if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) { [self.delegate onSocketConnectionStateChanged:webSocket.readyState]; } } - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload { NSString *string = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding]; NSLog(@"XTWebSocket Pong:%@", string); } #pragma mark - 其他 - - (void)dealloc { NSLog(@"LFC: dealloc: %@", self); [self close]; } #pragma mark -数据处理- -(void)onmessage:(NSDictionary *)msg{ NSLog(@"onmessage:%@",msg); // NSLog(@"%@",msg[@"chatId"]); // NSLog(@"%@",msg[@"fromId"]); //NSLog(@"%@",msg[@"content"]); NSMutableDictionary *mutablemsg = [msg mutableCopy]; if([self.userid isEqual:msg[@"fromId"]]){ [mutablemsg setObject:[NSNumber numberWithBool:YES] forKey:@"mine"]; // NSLog(@"----------------------11"); [ChatsStore.shareInstance deleteMineTmpMsg:msg];//删除本地临时消息 }else{ if([Friendchat isEqual:msg[@"type"]]){ [mutablemsg setObject:msg[@"fromId"] forKey:@"chatId"]; } } [ChatsStore.shareInstance reciveMsg:mutablemsg];//聊天窗信息 [ChatListStore.shareInstance reciveMsg:[mutablemsg copy]];//聊天窗列表 [[NSNotificationCenter defaultCenter] postNotificationName:nkonNewMessageNote object:mutablemsg]; NSString *messageType = msg[@"messageType"]; if ([messageType isEqualToString:MessageType_event]) { [[NSNotificationCenter defaultCenter] postNotificationName:nkonJoinGroupNote object:mutablemsg]; } } //消息已收到回执 -(void)sendRecNote:(NSDictionary *)msg{ NSDictionary *dic =@{ @"chatId":msg[@"chatId"], @"userId":self.userid, @"msgId":msg[@"id"], @"type":msg[@"type"] }; NSDictionary *sendInfo = @{ @"code":SendCode_RECMsg, @"message":dic, }; NSError *error; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:sendInfo options:0 error:&error]; if (!jsonData) { NSLog(@"Got an error: %@", error); } else { NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; // NSLog(@"jsonString:%@",jsonString); [self sendMsg:jsonString]; } } //收到已读消息 -(void)onRead:(NSDictionary *)dis{ [ChatsStore.shareInstance updatereadTime:dis]; } //异地登录 -(void)logoutAct{ NSLog(@"logoutAct"); [[LoginStateManager sharedManager] logout]; } #pragma mark 检测网络状态 -(void)startNetworkReachability { AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager]; __weak typeof(manager) weakManager = manager; [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { BOOL isReachable = weakManager.isReachable; self->_isReachable = isReachable; NSLog(@"XTWebSocket ReachabilityStatusChange: %d", isReachable); if (isReachable) { [self checkReconnectIfNeed]; } }]; [manager startMonitoring]; } - (BOOL)isReachable { return _isReachable; } - (void)checkReconnectIfNeed { NSLog(@"XTWebSocket checkReconnectIfNeed readyState: %ld", self.socket.readyState); switch (self.socket.readyState) { case SR_CONNECTING: break; case SR_OPEN: break; case SR_CLOSING: break; case SR_CLOSED:{ if (self.isReachable) { [self reConnectSocket]; } }break; } } @end