GWebSocket.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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. self.socket = nil;
  162. [self initWebSocket];
  163. // NSLog(@"reConnect1111---------:%ld",(long)self.reconnetCount);
  164. if(self.reconnetCount>15){
  165. NSString *token = [UDManager.shareInstance getSDManager:gkeytoken];
  166. if([token isKindOfClass:[NSString class]]&&token.length>10){
  167. [UserNetApi getUserinfo:^(int code, NSDictionary * res) {
  168. NSNumber *gcode=res[@"code"];
  169. NSLog(@"gcode11");
  170. if([gcode intValue]==401){//token有效
  171. [self logoutAct];
  172. }
  173. } fail:^(NSError * _Nonnull error) {
  174. NSLog(@"11%@", error);
  175. }];
  176. }
  177. }
  178. }
  179. // 关闭Socket
  180. - (void)close {
  181. [self destoryHeartbeat];
  182. [self.socket close];
  183. self.socket = nil;
  184. }
  185. #pragma mark -- SRWebSocketDelegate
  186. //收到服务器消息是回调
  187. - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
  188. //NSLog(@"----XTWebSocket didReceiveMessage:%@",message);
  189. if ([message isKindOfClass:[NSString class]]) {
  190. NSString *msg = (NSString *)message;
  191. NSError *error;
  192. self.pingCount = 0;
  193. self.reconnetCount = 0;
  194. if([msg isEqual:ping]){
  195. if(self.isFirstload){//第一次打开socket数据加载完成需要执行的
  196. }
  197. return;
  198. }
  199. // NSLog(@"didReceiveMessage:%@",msg);
  200. NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
  201. NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
  202. if (error) {
  203. NSLog(@"JSON解析错误: %@", error);
  204. } else {
  205. // NSLog(@"解析后的字典: %@", jsonDict);
  206. NSString *code =jsonDict[@"code"];
  207. if([code isEqualToString:SendCode_MESSAGE]){//收到消息数据
  208. [self onmessage:jsonDict[@"message"]];
  209. return;
  210. }
  211. if([code isEqualToString:SendCode_READ]){//已读消息
  212. [self onRead:jsonDict[@"message"]];
  213. return;
  214. }
  215. if([code isEqualToString:SendCode_OTHER_LOGIN]){//账号在其他地方登陆
  216. NSDictionary *mmsg = jsonDict[@"message"];
  217. NSString *uuid =mmsg[@"uuid"];
  218. if([uuid isEqualToString:_UUID]){
  219. return;
  220. }
  221. else{
  222. [self logoutAct];
  223. }
  224. return;
  225. }
  226. if([code isEqualToString:SendCode_NEW_FRIEND]){//好友申请
  227. NSLog(@"好友申请----------");
  228. [[NSNotificationCenter defaultCenter] postNotificationName: nkonAddFriendNote object:nil];
  229. return;
  230. }
  231. if([code isEqualToString:SendCode_GROUP_VALIDATE]){//群申请验证
  232. [[NSNotificationCenter defaultCenter] postNotificationName: nkonEXGroupdNote object:nil];
  233. return;
  234. }
  235. if([code isEqualToString:SendCode_WEBRTC_result]){//音视频通话结果
  236. [self onmessage:jsonDict[@"message"]];
  237. return;
  238. }
  239. if([code isEqualToString:SendCode_WEBRTC_CALL]){//音视频通话请求
  240. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  241. return;
  242. }
  243. if([code isEqualToString:SendCode_WEBRTC_CLOSE]){//关闭音视频通话
  244. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  245. return;
  246. }
  247. if([code isEqualToString:SendCode_WEBRTC_BUSY]){//音视频通话忙碌
  248. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  249. return;
  250. }
  251. if([code isEqualToString:SendCode_WEBRTC_xinling]){//webrtc信令交互
  252. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  253. return;
  254. }
  255. if([code isEqualToString:SendCode_deletemsg]){//删除消息
  256. return;
  257. }
  258. if([code isEqualToString:SendCode_WEBRTC_DFBUSY]){//对方正在忙
  259. [WebRTCStore.shareInstance reciveMsg:jsonDict[@"message"]];
  260. return;
  261. }
  262. if([code isEqualToString:SendCode_RECMsg]){//收到消息回执
  263. return;
  264. }
  265. }
  266. if(self.delegate){
  267. [self.delegate GWebSocketAction:message];
  268. }
  269. }
  270. }
  271. //连接成功
  272. - (void)webSocketDidOpen:(SRWebSocket *)webSocket{
  273. NSLog(@"-----XTWebSocket DidOpen");
  274. // 下面逻辑,根据业务情况处理
  275. if (self.socket != nil) {
  276. // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
  277. if (_socket.readyState == SR_OPEN) {
  278. //NSString *jsonString = @"{\"sid\": \"13b313a3-fea9-4e28-9e56-352458f7007f\"}";
  279. //[_socket sendString:jsonString error:nil]; //发送数据包
  280. [self sendReady];
  281. } else if (_socket.readyState == SR_CONNECTING) {
  282. NSLog(@"正在连接中,重连后其他方法会去自动同步数据");
  283. // 每隔2秒检测一次 socket.readyState 状态,检测 10 次左右
  284. // 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据
  285. // 如果 10 次都还是没连上的,那这个发送请求就丢失了,这种情况是服务器的问题了,小概率的
  286. // 代码有点长,我就写个逻辑在这里好了
  287. } else if (_socket.readyState == SR_CLOSING || _socket.readyState == SR_CLOSED) {
  288. // websocket 断开了,调用 reConnect 方法重连
  289. NSLog(@"websocket 断开了,调用 reConnect 方法重连");
  290. [self reConnect];
  291. }
  292. }
  293. }
  294. //连接失败的回调
  295. - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{
  296. NSLog(@"XTWebSocket didFailWithError %@",error);
  297. // 1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连
  298. // 2.判断调用层是否需要连接,例如用户都没在聊天界面,连接上去浪费流量
  299. // [self reConnect];
  300. }
  301. //连接断开的回调
  302. - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
  303. NSLog(@"XTWebSocket Close code %ld reason %@",(long)code,reason);
  304. }
  305. - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload {
  306. NSLog(@"XTWebSocket Pong");
  307. }
  308. #pragma mark - 其他 -
  309. - (void)dealloc {
  310. NSLog(@"LFC: dealloc: %@", self);
  311. [[NSNotificationCenter defaultCenter] removeObserver:self];
  312. }
  313. #pragma mark -数据处理-
  314. -(void)onmessage:(NSDictionary *)msg{
  315. NSLog(@"onmessage:%@",msg);
  316. // NSLog(@"%@",msg[@"chatId"]);
  317. // NSLog(@"%@",msg[@"fromId"]);
  318. //NSLog(@"%@",msg[@"content"]);
  319. NSMutableDictionary *mutablemsg = [msg mutableCopy];
  320. if([self.userid isEqual:msg[@"fromId"]]){
  321. [mutablemsg setObject:[NSNumber numberWithBool:YES] forKey:@"mine"];
  322. // NSLog(@"----------------------11");
  323. [ChatsStore.shareInstance deleteMineTmpMsg:msg];//删除本地临时消息
  324. }else{
  325. if([Friendchat isEqual:msg[@"type"]]){
  326. [mutablemsg setObject:msg[@"fromId"] forKey:@"chatId"];
  327. }
  328. }
  329. [ChatsStore.shareInstance reciveMsg:mutablemsg];//聊天窗信息
  330. [ChatListStore.shareInstance reciveMsg:[mutablemsg copy]];//聊天窗列表
  331. [[NSNotificationCenter defaultCenter] postNotificationName:nkonNewMessageNote object:mutablemsg];
  332. }
  333. //消息已收到回执
  334. -(void)sendRecNote:(NSDictionary *)msg{
  335. NSDictionary *dic =@{
  336. @"chatId":msg[@"chatId"],
  337. @"userId":self.userid,
  338. @"msgId":msg[@"id"],
  339. @"type":msg[@"type"]
  340. };
  341. NSDictionary *sendInfo = @{
  342. @"code":SendCode_RECMsg,
  343. @"message":dic,
  344. };
  345. NSError *error;
  346. NSData *jsonData = [NSJSONSerialization dataWithJSONObject:sendInfo options:0 error:&error];
  347. if (!jsonData) {
  348. NSLog(@"Got an error: %@", error);
  349. } else {
  350. NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
  351. // NSLog(@"jsonString:%@",jsonString);
  352. [self sendMsg:jsonString];
  353. }
  354. }
  355. //收到已读消息
  356. -(void)onRead:(NSDictionary *)dis{
  357. [ChatsStore.shareInstance updatereadTime:dis];
  358. }
  359. //异地登录
  360. -(void)logoutAct{
  361. NSLog(@"logoutAct");
  362. [UserNetApi logout:nil succ:^(int code, NSDictionary * res) {
  363. // NSLog(@"res:%@",res);
  364. [[NSNotificationCenter defaultCenter] postNotificationName: nkonLogoutSucc object:nil];
  365. } fail:^(NSError * _Nonnull error) {
  366. NSLog(@"");
  367. }];
  368. }
  369. @end