GWebSocket.m 14 KB

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