GWebSocket.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. //
  2. // GWebSocket.m
  3. // TUIContact
  4. //
  5. // Created by gan on 2025/3/24.
  6. //
  7. #import <Foundation/Foundation.h>
  8. #import "GWebSocket.h"
  9. #import <AFNetworking/AFNetworkReachabilityManager.h>
  10. #import "GDBManager.h"
  11. #import "ChatListStore.h"
  12. #import "ChatsStore.h"
  13. #import "WebRTCStore.h"
  14. #import "UserNetApi.h"
  15. #import "LoginStateManager.h"
  16. static int const kHeartbeatDuration = 10;
  17. static int const timeoutError = 5;
  18. static NSString *ping = @"{\"code\":0}";
  19. static NSString *redeady = @"{\"code\":1}";
  20. @interface GWebSocket ()<SRWebSocketDelegate>
  21. @property (nonatomic,strong) SRWebSocket *socket;
  22. @property (strong, nonatomic) NSTimer *heatBeat;
  23. @property (nonatomic,strong) NSString *serverIpString;
  24. @property (nonatomic,strong) NSString *userid;
  25. @property (nonatomic,strong) NSString *UUID;
  26. @end
  27. @implementation GWebSocket
  28. {
  29. BOOL _isReachable;
  30. }
  31. + (GWebSocket *_Nonnull)shareInstance{
  32. static id gShareInstance = nil;
  33. static dispatch_once_t onceToken;
  34. dispatch_once(&onceToken, ^{
  35. gShareInstance = [[self alloc] init];
  36. });
  37. return gShareInstance;
  38. }
  39. - (instancetype)init {
  40. if (self = [super init]) {
  41. NSUUID *uid = [NSUUID UUID];
  42. _UUID=uid.UUIDString;
  43. NSLog(@"_UUID:%@",_UUID);
  44. [self startNetworkReachability];
  45. [self startHeartbeat];
  46. [self addObserver];
  47. }
  48. return self;
  49. }
  50. #pragma mark - Public -
  51. - (void)connectWebSocket {
  52. [self initWebSocket];
  53. }
  54. - (void)closeWebSocket {
  55. // _UUID=nil;
  56. [self close];
  57. }
  58. - (void)sendMsg:(NSString *)msg {
  59. if (self.socket && self.socket.readyState == SR_OPEN) {
  60. // 只有在socket状态为SR_OPEN 时,才可以发送消息
  61. // 在socket状态不为SR_OPEN,可以将消息放进队列里,在websocket连上时,再发送
  62. NSLog(@"sendMsg:%@",msg);
  63. [self.socket sendString:msg error:nil];
  64. }
  65. else{
  66. NSLog(@"socket--------不可用");
  67. [self reConnectSocket];
  68. }
  69. // NSLog(@"_UUID:%@",_UUID);
  70. }
  71. #pragma mark -- WebSocket
  72. //初始化 WebSocket
  73. - (void)initWebSocket{
  74. NSDictionary *userinfo =[UDManager.shareInstance getDDManager:dkuserinfo];
  75. self.userid =userinfo[@"id"];
  76. NSString *token = (NSString *)[UDManager.shareInstance getSDManager:gkeytoken];
  77. if(_UUID==nil){
  78. NSUUID *uid = [NSUUID UUID];
  79. _UUID=uid.UUIDString;
  80. }
  81. NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@token=%@&client=mobile&uuid=%@",WebSocketUrl,token,_UUID]];
  82. //请求
  83. //NSLog(@"url:%@",url);
  84. NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
  85. //初始化请求`
  86. _socket = [[SRWebSocket alloc] initWithURLRequest:request];
  87. //代理协议`
  88. _socket.delegate = self;
  89. //直接连接
  90. [_socket open];
  91. }
  92. -(void)sendReady{
  93. NSLog(@"------%@",redeady);
  94. [self.socket sendString:redeady error:nil];
  95. }
  96. -(void)sendPing{
  97. [self.socket sendString:ping error:nil];
  98. }
  99. #pragma mark - Heart Timer -
  100. - (void)addObserver {
  101. // 进入后台
  102. [[NSNotificationCenter defaultCenter] addObserver:self
  103. selector:@selector(appDidEnterBackground)
  104. name:UIApplicationDidEnterBackgroundNotification
  105. object:nil];
  106. // 回到前台
  107. [[NSNotificationCenter defaultCenter] addObserver:self
  108. selector:@selector(appWillEnterForeground)
  109. name:UIApplicationWillEnterForegroundNotification
  110. object:nil];
  111. }
  112. - (void)appDidEnterBackground {
  113. [self destoryHeartbeat];
  114. }
  115. - (void)appWillEnterForeground {
  116. [self startHeartbeat];
  117. [self checkReconnectIfNeed];
  118. }
  119. //保活机制 探测包
  120. - (void)startHeartbeat {
  121. self.heatBeat = [NSTimer scheduledTimerWithTimeInterval:kHeartbeatDuration
  122. target:self
  123. selector:@selector(heartbeatAction)
  124. userInfo:nil
  125. repeats:YES];
  126. [[NSRunLoop currentRunLoop] addTimer:self.heatBeat forMode:NSRunLoopCommonModes];
  127. }
  128. //断开连接时销毁心跳
  129. - (void)destoryHeartbeat{
  130. [_heatBeat invalidate];
  131. _heatBeat = nil;
  132. }
  133. // 发送心跳
  134. - (void)heartbeatAction {
  135. NSLog(@"XTWebSocket heartbeatAction readyState: %ld", self.socket.readyState);
  136. BOOL isLoggedIn = [LoginStateManager sharedManager].isLoggedIn;
  137. if (!isLoggedIn) {
  138. NSLog(@"XTWebSocket heartbeatAction not login!");
  139. return;
  140. }
  141. switch (self.socket.readyState) {
  142. case SR_CONNECTING:
  143. break;
  144. case SR_OPEN:
  145. [self sendPing];
  146. break;
  147. case SR_CLOSING:
  148. break;
  149. case SR_CLOSED:
  150. [self reConnectSocket];
  151. break;
  152. }
  153. }
  154. //重连机制
  155. - (void)reConnectSocket{
  156. [self closeWebSocket];
  157. [self initWebSocket];
  158. }
  159. // 关闭Socket
  160. - (void)close {
  161. [self destoryHeartbeat];
  162. [self.socket close];
  163. self.socket = nil;
  164. }
  165. #pragma mark -- SRWebSocketDelegate
  166. //收到服务器消息是回调
  167. - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
  168. NSLog(@"----XTWebSocket didReceiveMessage:%@",message);
  169. if ([message isKindOfClass:[NSString class]]) {
  170. NSString *msg = (NSString *)message;
  171. NSError *error;
  172. if([msg isEqual:ping]){
  173. return;
  174. }
  175. // NSLog(@"didReceiveMessage:%@",msg);
  176. NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
  177. NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  178. if (error) {
  179. NSLog(@"JSON解析错误: %@", error);
  180. } else {
  181. // NSLog(@"解析后的字典: %@", jsonDict);
  182. NSString *code =jsonDict[@"code"];
  183. if([code isEqualToString:SendCode_MESSAGE]){//收到消息数据
  184. [self onmessage:jsonDict[@"message"]];
  185. return;
  186. }
  187. if([code isEqualToString:SendCode_READ]){//已读消息
  188. [self onRead:jsonDict[@"message"]];
  189. return;
  190. }
  191. if([code isEqualToString:SendCode_OTHER_LOGIN]){//账号在其他地方登陆
  192. NSDictionary *mmsg = jsonDict[@"message"];
  193. NSString *uuid =mmsg[@"uuid"];
  194. if([uuid isEqualToString:_UUID]){
  195. return;
  196. }
  197. else{
  198. [self logoutAct];
  199. }
  200. return;
  201. }
  202. if([code isEqualToString:SendCode_NEW_FRIEND]){//好友申请
  203. NSLog(@"好友申请----------");
  204. [[NSNotificationCenter defaultCenter] postNotificationName: nkonAddFriendNote object:nil];
  205. return;
  206. }
  207. if([code isEqualToString:SendCode_GROUP_VALIDATE]){//群申请验证
  208. [[NSNotificationCenter defaultCenter] postNotificationName: nkonEXGroupdNote object:nil];
  209. return;
  210. }
  211. if([code isEqualToString:SendCode_WEBRTC_result]){//音视频通话结果
  212. [self onmessage:jsonDict[@"message"]];
  213. return;
  214. }
  215. if([code isEqualToString:SendCode_WEBRTC_CALL]){//音视频通话请求
  216. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  217. return;
  218. }
  219. if([code isEqualToString:SendCode_WEBRTC_CLOSE]){//关闭音视频通话
  220. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  221. return;
  222. }
  223. if([code isEqualToString:SendCode_WEBRTC_BUSY]){//音视频通话忙碌
  224. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  225. return;
  226. }
  227. if([code isEqualToString:SendCode_WEBRTC_xinling]){//webrtc信令交互
  228. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  229. return;
  230. }
  231. if([code isEqualToString:SendCode_deletemsg]){//删除消息
  232. return;
  233. }
  234. if([code isEqualToString:SendCode_WEBRTC_DFBUSY]){//对方正在忙
  235. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  236. return;
  237. }
  238. if([code isEqualToString:SendCode_RECMsg]){//收到消息回执
  239. return;
  240. }
  241. }
  242. }
  243. }
  244. //连接成功
  245. - (void)webSocketDidOpen:(SRWebSocket *)webSocket{
  246. NSLog(@"-----XTWebSocket DidOpen: %ld", webSocket.readyState);
  247. switch (webSocket.readyState) {
  248. case SR_CONNECTING:
  249. break;
  250. case SR_OPEN:{
  251. [self sendReady];
  252. if (![_heatBeat isValid]) {
  253. [self destoryHeartbeat];
  254. [self startHeartbeat];
  255. }
  256. }break;
  257. case SR_CLOSING:
  258. break;
  259. case SR_CLOSED:
  260. break;
  261. }
  262. if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) {
  263. [self.delegate onSocketConnectionStateChanged:webSocket.readyState];
  264. }
  265. }
  266. //连接失败的回调
  267. - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{
  268. NSLog(@"XTWebSocket didFailWithError %@",error);
  269. // 1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连
  270. // 2.判断调用层是否需要连接,例如用户都没在聊天界面,连接上去浪费流量
  271. if (self.isReachable) {
  272. [self reConnectSocket];
  273. }
  274. if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) {
  275. [self.delegate onSocketConnectionStateChanged:webSocket.readyState];
  276. }
  277. }
  278. //连接断开的回调
  279. - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
  280. NSLog(@"XTWebSocket Close code %ld reason %@",(long)code,reason);
  281. if (self.delegate && [self.delegate respondsToSelector:@selector(onSocketConnectionStateChanged:)]) {
  282. [self.delegate onSocketConnectionStateChanged:webSocket.readyState];
  283. }
  284. }
  285. - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload {
  286. NSString *string = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
  287. NSLog(@"XTWebSocket Pong:%@", string);
  288. }
  289. #pragma mark - 其他 -
  290. - (void)dealloc {
  291. NSLog(@"LFC: dealloc: %@", self);
  292. [self close];
  293. }
  294. #pragma mark -数据处理-
  295. -(void)onmessage:(NSDictionary *)msg{
  296. NSLog(@"onmessage:%@",msg);
  297. // NSLog(@"%@",msg[@"chatId"]);
  298. // NSLog(@"%@",msg[@"fromId"]);
  299. //NSLog(@"%@",msg[@"content"]);
  300. NSMutableDictionary *mutablemsg = [msg mutableCopy];
  301. if([self.userid isEqual:msg[@"fromId"]]){
  302. [mutablemsg setObject:[NSNumber numberWithBool:YES] forKey:@"mine"];
  303. // NSLog(@"----------------------11");
  304. [ChatsStore.shareInstance deleteMineTmpMsg:msg];//删除本地临时消息
  305. }else{
  306. if([Friendchat isEqual:msg[@"type"]]){
  307. [mutablemsg setObject:msg[@"fromId"] forKey:@"chatId"];
  308. }
  309. }
  310. [ChatsStore.shareInstance reciveMsg:mutablemsg];//聊天窗信息
  311. [ChatListStore.shareInstance reciveMsg:[mutablemsg copy]];//聊天窗列表
  312. [[NSNotificationCenter defaultCenter] postNotificationName:nkonNewMessageNote object:mutablemsg];
  313. NSString *messageType = msg[@"messageType"];
  314. if ([messageType isEqualToString:MessageType_event]) {
  315. [[NSNotificationCenter defaultCenter] postNotificationName:nkonJoinGroupNote object:mutablemsg];
  316. }
  317. }
  318. //消息已收到回执
  319. -(void)sendRecNote:(NSDictionary *)msg{
  320. NSDictionary *dic =@{
  321. @"chatId":msg[@"chatId"],
  322. @"userId":self.userid,
  323. @"msgId":msg[@"id"],
  324. @"type":msg[@"type"]
  325. };
  326. NSDictionary *sendInfo = @{
  327. @"code":SendCode_RECMsg,
  328. @"message":dic,
  329. };
  330. NSError *error;
  331. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:sendInfo options:0 error:&error];
  332. if (!jsonData) {
  333. NSLog(@"Got an error: %@", error);
  334. } else {
  335. NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
  336. // NSLog(@"jsonString:%@",jsonString);
  337. [self sendMsg:jsonString];
  338. }
  339. }
  340. //收到已读消息
  341. -(void)onRead:(NSDictionary *)dis{
  342. [ChatsStore.shareInstance updatereadTime:dis];
  343. }
  344. //异地登录
  345. -(void)logoutAct{
  346. NSLog(@"logoutAct");
  347. [[LoginStateManager sharedManager] logout];
  348. }
  349. #pragma mark 检测网络状态
  350. -(void)startNetworkReachability {
  351. AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
  352. __weak typeof(manager) weakManager = manager;
  353. [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
  354. BOOL isReachable = weakManager.isReachable;
  355. self->_isReachable = isReachable;
  356. NSLog(@"XTWebSocket ReachabilityStatusChange: %d", isReachable);
  357. if (isReachable) {
  358. [self checkReconnectIfNeed];
  359. }
  360. }];
  361. [manager startMonitoring];
  362. }
  363. - (BOOL)isReachable {
  364. return _isReachable;
  365. }
  366. - (void)checkReconnectIfNeed {
  367. NSLog(@"XTWebSocket checkReconnectIfNeed readyState: %ld", self.socket.readyState);
  368. switch (self.socket.readyState) {
  369. case SR_CONNECTING:
  370. break;
  371. case SR_OPEN:
  372. break;
  373. case SR_CLOSING:
  374. break;
  375. case SR_CLOSED:{
  376. if (self.isReachable) {
  377. [self reConnectSocket];
  378. }
  379. }break;
  380. }
  381. }
  382. @end