| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- //
- // GWebSocket.m
- // TUIContact
- //
- // Created by gan on 2025/3/24.
- //
- #import <Foundation/Foundation.h>
- #import "GWebSocket.h"
- #import <AFNetworking/AFNetworkReachabilityManager.h>
- #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 ()<SRWebSocketDelegate>
- @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];
- }
- //消息已收到回执
- -(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
|