GWebSocket.m 13 KB

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