WebSocketManager.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. //
  2. // WebSocketManager.m
  3. // Anchor
  4. //
  5. // Created by 翟玉磊 on 2019/3/26.
  6. // Copyright © 2019 翟玉磊. All rights reserved.
  7. //
  8. #import "WebSocketManager.h"
  9. #import "ZYLURLRequestGenerator.h"
  10. #import "LFCGzipUtility.h"
  11. @interface WebSocketManager()<SRWebSocketDelegate>
  12. @property (nonatomic, strong) NSTimer *heartBeatTimer; //心跳定时器
  13. @property (nonatomic, strong) NSTimer *netWorkTestingTimer; //没有网络的时候检测网络定时器
  14. @property (nonatomic, strong) dispatch_queue_t queue; //数据请求队列(串行队列)
  15. @property (nonatomic, assign) NSTimeInterval reConnectTime; //重连时间
  16. @property (nonatomic, strong) NSMutableArray *sendDataArray; //存储要发送给服务端的数据
  17. @property (nonatomic, assign) BOOL isActivelyClose; //用于判断是否主动关闭长连接,如果是主动断开连接,连接失败的代理中,就不用执行 重新连接方法
  18. /// 保存连接url
  19. @property (nonatomic, readwrite, copy) NSString *connectUrl;
  20. @property (nonatomic, readwrite, strong) IMConnectCompletionHandler connectCompletionHandler;
  21. @property (nonatomic, readwrite, strong) IMCloseCompletionHandler closeCompletionHandler;
  22. @end
  23. @implementation WebSocketManager
  24. //单例
  25. + (instancetype)sharedSocketManager
  26. {
  27. static WebSocketManager *_instace = nil;
  28. static dispatch_once_t onceToken;
  29. dispatch_once(&onceToken, ^{
  30. _instace = [[self alloc] init];
  31. });
  32. return _instace;
  33. }
  34. - (instancetype)init
  35. {
  36. self = [super init];
  37. if(self)
  38. {
  39. self.reConnectTime = 0;
  40. self.isActivelyClose = NO;
  41. self.queue = dispatch_queue_create("BF",NULL);
  42. self.sendDataArray = [[NSMutableArray alloc] init];
  43. }
  44. return self;
  45. }
  46. #pragma mark - NSTimer
  47. //初始化心跳
  48. - (void)initHeartBeat
  49. {
  50. //心跳没有被关闭
  51. if(self.heartBeatTimer)
  52. {
  53. return;
  54. }
  55. [self destoryHeartBeat];
  56. WeakSelf
  57. dispatch_main_async_safe(^{
  58. // dispatch_async(dispatch_get_main_queue(), ^{
  59. weakSelf.heartBeatTimer = [NSTimer timerWithTimeInterval:20 target:weakSelf selector:@selector(senderheartBeat) userInfo:nil repeats:true];
  60. [[NSRunLoop currentRunLoop]addTimer:weakSelf.heartBeatTimer forMode:NSRunLoopCommonModes];
  61. });
  62. [self senderheartBeat];
  63. }
  64. //取消心跳
  65. - (void)destoryHeartBeat
  66. {
  67. WeakSelf
  68. dispatch_main_async_safe(^{
  69. // dispatch_async(dispatch_get_main_queue(), ^{
  70. if(weakSelf.heartBeatTimer)
  71. {
  72. NSLog(@"取消心跳");
  73. [weakSelf.heartBeatTimer invalidate];
  74. weakSelf.heartBeatTimer = nil;
  75. }
  76. });
  77. }
  78. //没有网络的时候开始定时 -- 用于网络检测
  79. - (void)noNetWorkStartTestingTimer
  80. {
  81. WeakSelf
  82. dispatch_main_async_safe(^{
  83. // dispatch_async(dispatch_get_main_queue(), ^{
  84. NSLog(@"取消网络监测");
  85. weakSelf.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
  86. [[NSRunLoop currentRunLoop] addTimer:weakSelf.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
  87. });
  88. }
  89. //取消网络检测
  90. - (void)destoryNetWorkStartTesting
  91. {
  92. WeakSelf
  93. dispatch_main_async_safe(^{
  94. //dispatch_async(dispatch_get_main_queue(), ^{
  95. if(weakSelf.netWorkTestingTimer)
  96. {
  97. [weakSelf.netWorkTestingTimer invalidate];
  98. weakSelf.netWorkTestingTimer = nil;
  99. }
  100. });
  101. }
  102. #pragma mark - private -- webSocket相关方法
  103. //发送心跳
  104. - (void)senderheartBeat
  105. {
  106. //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
  107. WeakSelf
  108. dispatch_main_async_safe(^{
  109. if(weakSelf.webSocket.readyState == SR_OPEN)
  110. {
  111. NSString *text = @"ping";
  112. NSData * gZipData = [LFCGzipUtility gzipData:[text dataUsingEncoding:NSUTF8StringEncoding]];
  113. [weakSelf.webSocket send:gZipData];
  114. NSLog(@"发送ping:%@",text);
  115. }
  116. });
  117. }
  118. //定时检测网络
  119. - (void)noNetWorkStartTesting
  120. {
  121. //有网络
  122. if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
  123. {
  124. //关闭网络检测定时器
  125. [self destoryNetWorkStartTesting];
  126. //开始重连
  127. [self reConnectServer];
  128. }
  129. }
  130. //建立长连接
  131. - (void)connectServerWithUrl:(NSString *)url connectCompletionHandler:(nonnull IMConnectCompletionHandler)completionHandler
  132. {
  133. if (StringIsEmpty(_connectUrl)) {
  134. _connectUrl = url;
  135. }
  136. self.isActivelyClose = NO;
  137. if(_webSocket)
  138. {
  139. _webSocket = nil;
  140. }
  141. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
  142. [[ZYLURLRequestGenerator sharedInstance] setCommonRequestHeaderForRequest:request];
  143. self.webSocket = [[SRWebSocket alloc] initWithURLRequest:request];
  144. self.webSocket.delegate = self;
  145. [self.webSocket open];
  146. NSLog(@"建立连接");
  147. self.connectCompletionHandler = completionHandler;
  148. }
  149. //重新连接服务器
  150. - (void)reConnectServer
  151. {
  152. if(self.webSocket.readyState == SR_OPEN)
  153. {
  154. return;
  155. }
  156. if(self.reConnectTime > 1024) //重连10次 2^10 = 1024
  157. {
  158. self.reConnectTime = 0;
  159. return;
  160. }
  161. WeakSelf
  162. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reConnectTime *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  163. if(weakSelf.webSocket.readyState == SR_OPEN && weakSelf.webSocket.readyState == SR_CONNECTING)
  164. {
  165. return;
  166. }
  167. [weakSelf connectServerWithUrl:self.connectUrl connectCompletionHandler:self.connectCompletionHandler];
  168. NSLog(@"正在重连......");
  169. if(weakSelf.reConnectTime == 0) //重连时间2的指数级增长
  170. {
  171. weakSelf.reConnectTime = 2;
  172. }
  173. else
  174. {
  175. weakSelf.reConnectTime *= 2;
  176. }
  177. });
  178. }
  179. //关闭连接
  180. - (void)SRWebSocketCloseCompletionHandler:(IMCloseCompletionHandler)completionHandler
  181. {
  182. self.isActivelyClose = YES;
  183. self.closeCompletionHandler = completionHandler;
  184. [self webSocketClose];
  185. //关闭心跳定时器
  186. [self destoryHeartBeat];
  187. //关闭网络检测定时器
  188. [self destoryNetWorkStartTesting];
  189. }
  190. //关闭连接
  191. - (void)webSocketClose
  192. {
  193. if(self.webSocket)
  194. {
  195. [self.webSocket close];
  196. NSLog(@"close:%ld",self.webSocket.readyState);
  197. _webSocket = nil;
  198. }
  199. }
  200. //发送数据给服务器
  201. - (void)sendDataToServer:(id)data
  202. {
  203. NSData * gZipData = [LFCGzipUtility gzipData:data];
  204. [self.sendDataArray addObject:gZipData];
  205. [self sendeDataToServer];
  206. }
  207. - (void)sendeDataToServer
  208. {
  209. WeakSelf
  210. //把数据放到一个请求队列中
  211. dispatch_async(self.queue, ^{
  212. //没有网络
  213. if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable)
  214. {
  215. //开启网络检测定时器
  216. [weakSelf noNetWorkStartTestingTimer];
  217. }
  218. else //有网络
  219. {
  220. if(weakSelf.webSocket != nil)
  221. {
  222. // 只有长连接OPEN开启状态才能调 send 方法,不然会Crash
  223. if(weakSelf.webSocket.readyState == SR_OPEN)
  224. {
  225. if (weakSelf.sendDataArray.count > 0)
  226. {
  227. NSString *data = weakSelf.sendDataArray[0];
  228. [weakSelf.webSocket send:data]; //发送数据
  229. [weakSelf.sendDataArray removeObjectAtIndex:0];
  230. if([weakSelf.sendDataArray count] > 0)
  231. {
  232. [weakSelf sendeDataToServer];
  233. }
  234. }
  235. }
  236. else if (weakSelf.webSocket.readyState == SR_CONNECTING) //正在连接
  237. {
  238. NSLog(@"正在连接中,重连后会去自动同步数据");
  239. }
  240. else if (weakSelf.webSocket.readyState == SR_CLOSING || weakSelf.webSocket.readyState == SR_CLOSED) //断开连接
  241. {
  242. //调用 reConnectServer 方法重连,连接成功后 继续发送数据
  243. [weakSelf reConnectServer];
  244. }
  245. }
  246. else
  247. {
  248. [weakSelf connectServerWithUrl:self.connectUrl connectCompletionHandler:self.connectCompletionHandler]; //连接服务器
  249. }
  250. }
  251. });
  252. }
  253. #pragma mark - SRWebSocketDelegate -- webSockect代理
  254. //连接成功回调
  255. - (void)webSocketDidOpen:(SRWebSocket *)webSocket
  256. {
  257. NSLog(@"webSocket === 连接成功");
  258. if (self.connectCompletionHandler) {
  259. self.connectCompletionHandler(0, @"连接成功");
  260. self.connectCompletionHandler = nil;
  261. }
  262. [self initHeartBeat]; //开启心跳
  263. //如果有尚未发送的数据,继续向服务端发送数据
  264. if ([self.sendDataArray count] > 0){
  265. [self sendeDataToServer];
  266. }
  267. }
  268. //连接失败回调
  269. - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
  270. {
  271. NSLog(@"webscoket连接失败回调:%ld",webSocket.readyState);
  272. //用户主动断开连接,就不去进行重连
  273. if(self.isActivelyClose)
  274. {
  275. if (self.closeCompletionHandler) {
  276. self.closeCompletionHandler(0, @"离开聊天队列成功");
  277. self.closeCompletionHandler = nil;
  278. }
  279. return;
  280. }
  281. [self destoryHeartBeat]; //断开连接时销毁心跳
  282. NSLog(@"连接失败,这里可以实现掉线自动重连,要注意以下几点");
  283. NSLog(@"1.判断当前网络环境,如果断网了就不要连了,等待网络到来,在发起重连");
  284. NSLog(@"3.连接次数限制,如果连接失败了,重试10次左右就可以了");
  285. //判断网络环境
  286. if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) //没有网络
  287. {
  288. [self noNetWorkStartTestingTimer];//开启网络检测定时器
  289. }
  290. else //有网络
  291. {
  292. [self reConnectServer];//连接失败就重连
  293. }
  294. }
  295. //连接关闭,注意连接关闭不是连接断开,关闭是 [socket close] 客户端主动关闭,断开可能是断网了,被动断开的。
  296. - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean
  297. {
  298. NSLog(@"webscoket连接关闭回调:%ld",webSocket.readyState);
  299. // 在这里判断 webSocket 的状态 是否为 open , 大家估计会有些奇怪 ,因为我们的服务器都在海外,会有些时间差,经过测试,我们在进行某次连接的时候,上次重连的回调刚好回来,而本次重连又成功了,就会误以为,本次没有重连成功,而再次进行重连,就会出现问题,所以在这里做了一下判断
  300. if(self.webSocket.readyState == SR_OPEN || self.isActivelyClose)
  301. {
  302. if (self.closeCompletionHandler) {
  303. self.closeCompletionHandler(0, @"离开聊天队列成功");
  304. self.closeCompletionHandler = nil;
  305. }
  306. return;
  307. }
  308. NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",code,reason,wasClean);
  309. [self destoryHeartBeat]; //断开连接时销毁心跳
  310. //判断网络环境
  311. if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) //没有网络
  312. {
  313. [self noNetWorkStartTestingTimer];//开启网络检测
  314. }
  315. else //有网络
  316. {
  317. [self reConnectServer];//连接失败就重连
  318. }
  319. }
  320. //该函数是接收服务器发送的pong消息,其中最后一个参数是接受pong消息的
  321. -(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData*)pongPayload
  322. {
  323. NSString* reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
  324. NSLog(@"reply === 收到后台心跳回复 Data:%@",reply);
  325. }
  326. //收到服务器发来的数据
  327. - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message
  328. {
  329. /// 这里使用Gzip解压 有些公司返回不是压缩包 根据实际处理
  330. NSData *datas = [LFCGzipUtility ungzipData:message];
  331. NSString * str = [[NSString alloc] initWithData:datas encoding:NSUTF8StringEncoding];
  332. if ([@"pong" isEqualToString:str]) {
  333. NSLog(@"验证心跳成功:%@", str);
  334. return;
  335. }
  336. /*根据具体的业务做具体的处理*/
  337. if ([self.delegate respondsToSelector:@selector(webSocketManagerDidReceiveMessageWithObject:)]) {
  338. [self.delegate webSocketManagerDidReceiveMessageWithObject:[str jsonValueDecoded]];
  339. }
  340. }
  341. @end