KSTargetWebSocketManager.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //
  2. // KSTargetWebSocketManager.m
  3. // KulexiuForStudent
  4. //
  5. // Created by 王智 on 2024/8/13.
  6. //
  7. #import "KSTargetWebSocketManager.h"
  8. #import <AFNetworkReachabilityManager.h>
  9. /*自定义打印 宏*/
  10. #ifdef DEBUG
  11. #define KSNSLog(format, ...) do { \
  12. fprintf(stderr, "<%s : %d> %s\n", \
  13. [[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], \
  14. __LINE__, __func__); \
  15. (NSLog)((format), ##__VA_ARGS__); \
  16. fprintf(stderr, "-------\n"); \
  17. } while (0)
  18. #else
  19. #define KSNSLog( ...)
  20. #endif
  21. typedef NS_ENUM(NSUInteger, SocketDataType) {
  22. distributeOrder,
  23. cancelCall,
  24. orderLost,
  25. changeDeviceType,
  26. };
  27. @interface KSTargetWebSocketManager ()<SRWebSocketDelegate>
  28. {
  29. NSTimer * heartBeat;
  30. NSTimeInterval reConnectTime;
  31. SocketDataType type;
  32. }
  33. @property (nonatomic,strong) SRWebSocket *socket;
  34. @end
  35. @implementation KSTargetWebSocketManager
  36. - (instancetype)init {
  37. self = [super init];
  38. if (self) {
  39. }
  40. return self;
  41. }
  42. - (void)registerNetworkNotifications{
  43. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChangedNote:) name:AFNetworkingReachabilityDidChangeNotification object:nil];
  44. }
  45. - (void)networkChangedNote:(NSNotification *)note{
  46. AFNetworkReachabilityStatus status = [note.userInfo[AFNetworkingReachabilityNotificationStatusItem] integerValue];
  47. switch (status) {
  48. case AFNetworkReachabilityStatusUnknown:
  49. NSLog(@"网络类型:未知网络");
  50. break;
  51. case AFNetworkReachabilityStatusNotReachable:
  52. NSLog(@"网络类型:断网");
  53. break;
  54. case AFNetworkReachabilityStatusReachableViaWWAN:
  55. NSLog(@"网络类型:数据流量");
  56. [self SRWebSocketOpen];
  57. break;
  58. case AFNetworkReachabilityStatusReachableViaWiFi:
  59. NSLog(@"网络类型:WIFI");
  60. [self SRWebSocketOpen];
  61. break;
  62. }
  63. }
  64. -(void)SRWebSocketOpen {
  65. //如果是同一个url return
  66. if (self.socket) {
  67. if (self.socket.readyState == SR_OPEN) {
  68. [self handleConnectionStatus:YES];
  69. return;
  70. }
  71. else {
  72. self.socket = nil;
  73. }
  74. }
  75. /** /userId */
  76. NSString *connectedUrl = [NSString stringWithFormat:@"%@/%@", SOCKET_URL, UserDefault(UIDKey)];
  77. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:connectedUrl]];
  78. request.allHTTPHeaderFields = @{@"Authorization":[NSString stringWithFormat:@"%@ %@", UserDefault(Token_type), UserDefault(TokenKey)]};
  79. self.socket = [[SRWebSocket alloc] initWithURLRequest:
  80. request];//这里填写你服务器的地址
  81. NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
  82. self.socket.delegate = self; //实现这个 SRWebSocketDelegate 协议
  83. [self.socket open]; //open 就是直接连接了
  84. }
  85. - (void)SRWebSocketClose {
  86. if (self.socket) {
  87. [self.socket close];
  88. self.socket = nil;
  89. //断开连接时销毁心跳
  90. [self destoryHeartBeat];
  91. }
  92. }
  93. #pragma mark - socket delegate
  94. - (void)webSocketDidOpen:(SRWebSocket *)webSocket {
  95. //每次正常连接的时候清零重连时间
  96. reConnectTime = 0;
  97. //开启心跳
  98. [self initHeartBeat];
  99. if (webSocket == self.socket) {
  100. NSLog(@"************************** socket 连接成功************************** ");
  101. [self handleConnectionStatus:YES];
  102. }
  103. }
  104. - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
  105. if (webSocket == self.socket) {
  106. NSLog(@"************************** socket 连接失败************************** ");
  107. _socket = nil;
  108. //连接失败就重连
  109. [self reConnect];
  110. }
  111. }
  112. - (void)handleConnectionStatus:(BOOL)isSuccess {
  113. if (self.connectionStatus) {
  114. self.connectionStatus(isSuccess);
  115. }
  116. }
  117. - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
  118. if (webSocket == self.socket) {
  119. NSLog(@"************************** socket连接断开************************** ");
  120. NSLog(@"被关闭连接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
  121. [self SRWebSocketClose];
  122. [self handleConnectionStatus:NO];
  123. }
  124. }
  125. /*该函数是接收服务器发送的pong消息,其中最后一个是接受pong消息的,
  126. 在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
  127. 用于每隔一段时间通知一次服务端,客户端还是在线,这个心跳包其实就是一个ping消息,
  128. 我的理解就是建立一个定时器,每隔十秒或者十五秒向服务端发送一个ping消息,这个消息可是是空的
  129. */
  130. -(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
  131. NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
  132. NSLog(@"reply===%@",reply);
  133. }
  134. #pragma mark - 收到的回调
  135. - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
  136. if (webSocket == self.socket) {
  137. NSLog(@"************************** socket收到数据了************************** ");
  138. NSLog(@"message:%@",message);
  139. if(!message){
  140. return;
  141. }
  142. [self handleReceivedMessage:message];
  143. }
  144. }
  145. - (void)handleReceivedMessage:(id)message {
  146. if(self.didReceiveMessage){
  147. self.didReceiveMessage(message);
  148. }
  149. }
  150. //重连机制
  151. - (void)reConnect
  152. {
  153. [self SRWebSocketClose];
  154. //超过一分钟就不再重连 所以只会重连5次 2^5 = 64
  155. NSLog(@"reConnectTime-----%.0f",reConnectTime);
  156. if (reConnectTime > 60) {
  157. //您的网络状况不是很好,请检查网络后重试
  158. [self handleConnectionStatus:NO];
  159. return;
  160. }
  161. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  162. self.socket = nil;
  163. [self SRWebSocketOpen];
  164. NSLog(@"重连");
  165. });
  166. //重连时间2的指数级增长
  167. if (reConnectTime == 0) {
  168. reConnectTime = 2;
  169. }else{
  170. reConnectTime *= 2;
  171. }
  172. }
  173. //初始化心跳
  174. - (void)initHeartBeat
  175. {
  176. dispatch_main_async_safe(^{
  177. [self destoryHeartBeat];
  178. self->heartBeat = [NSTimer timerWithTimeInterval:30 target:self selector:@selector(ping) userInfo:nil repeats:YES];
  179. //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
  180. [[NSRunLoop currentRunLoop]addTimer:self->heartBeat forMode:NSRunLoopCommonModes];
  181. })
  182. }
  183. //取消心跳
  184. - (void)destoryHeartBeat
  185. {
  186. dispatch_main_async_safe(^{
  187. if (self->heartBeat) {
  188. if ([self->heartBeat respondsToSelector:@selector(isValid)]){
  189. if ([self->heartBeat isValid]){
  190. [self->heartBeat invalidate];
  191. self->heartBeat = nil;
  192. }
  193. }
  194. }
  195. })
  196. }
  197. //pingPong
  198. - (void)ping{
  199. if (self.socket.readyState == SR_OPEN) {
  200. [self.socket sendPing:nil error:nil];
  201. }
  202. }
  203. - (void)sendData:(id)parmData {
  204. dispatch_queue_t queue = dispatch_queue_create("ks_websocket_queue", NULL);
  205. dispatch_async(queue, ^{
  206. if (self.socket != nil) {
  207. // 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
  208. if (self.socket.readyState == SR_OPEN) {
  209. // 发送数据
  210. if ([parmData isKindOfClass:[NSString class]]) {
  211. [self.socket sendString:parmData error:nil];
  212. }
  213. else if ([parmData isKindOfClass:[NSData class]]) {
  214. [self.socket sendData:parmData error:nil];
  215. }
  216. }
  217. else {
  218. [self SRWebSocketClose];
  219. [self handleConnectionStatus:NO];
  220. }
  221. /*else if (weakSelf.socket.readyState == SR_CONNECTING) {
  222. [weakSelf reConnect];
  223. } else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
  224. // websocket 断开了,调用 reConnect 方法重连
  225. NSLog(@"重连");
  226. [weakSelf reConnect];
  227. }*/
  228. } else {
  229. // 这里要看你的具体业务需求;不过一般情况下,调用发送数据还是希望能把数据发送出去,所以可以再次打开链接;不用担心这里会有多个socketopen;因为如果当前有socket存在,会停止创建哒
  230. [self SRWebSocketOpen];
  231. [self handleConnectionStatus:NO];
  232. }
  233. });
  234. }
  235. -(SRReadyState)socketReadyState{
  236. return self.socket.readyState;
  237. }
  238. -(void)dealloc{
  239. [[NSNotificationCenter defaultCenter] removeObserver:self];
  240. NSLog(@"SocketRocketUtility dealloced");
  241. }
  242. @end