GiGaMaskTime/GIGA/Common/SocketRocketUtility/GiGaSocketRocketUtility.m

249 lines
7.6 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// GiGaSocketRocketUtility.m
// LXAnimationTest
//
// Created by lianxiang on 2018/8/17.
// Copyright © 2018年 com.giga.ios. All rights reserved.
//
#import "GiGaSocketRocketUtility.h"
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
NSString * const kNeedPayOrderNote = @"kNeedPayOrderNote";//发送的通知名
NSString * const kWebSocketDidOpenNote = @"kWebSocketdidReceiveMessageNote";
NSString * const kWebSocketDidCloseNote = @"kWebSocketDidCloseNote";
NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote";
@interface GiGaSocketRocketUtility()<SRWebSocketDelegate>
{
int _index;
NSTimer *heartBeat;
NSTimeInterval reConnectTime;
}
@property (nonatomic,strong) SRWebSocket *socket;
@property (nonatomic,copy) NSString *urlString;
@end
@implementation GiGaSocketRocketUtility
+(GiGaSocketRocketUtility *)shareInstance{
static GiGaSocketRocketUtility *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
//开启链接
-(void)srWebSocketOpenWithURLString:(NSString *)url{
if (!self.socket) {
return;
}
if (!url) {
return;
}
self.urlString = url;
self.socket = [[SRWebSocket alloc] initWithURLRequest: [NSURLRequest requestWithURL:[NSURL URLWithString:url]]] ;
NSLog(@"请求的websocket地址%@",self.socket.url.absoluteString);
self.socket.delegate = self;
[self.socket open];
}
//关闭连接
-(void)srWebSocketClose{
if (self.socket) {
[self.socket close];
self.socket = nil;
[self removeHeartBeat];
}
}
//移除心跳
-(void)removeHeartBeat{
dispatch_main_async_safe(^{
if (self->heartBeat) {
if ([self->heartBeat respondsToSelector:@selector(isValid)]) {
if ([self->heartBeat isValid]) {
[self->heartBeat invalidate];
self->heartBeat = nil;
}
}
}
}
)
}
//初始化心跳
- (void)initHeartBeat
{
dispatch_main_async_safe(^{
[self removeHeartBeat];
//心跳设置为3分钟NAT超时一般为5分钟
self->heartBeat = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(sentheart) userInfo:nil repeats:YES];
//和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
[[NSRunLoop currentRunLoop] addTimer:self->heartBeat forMode:NSRunLoopCommonModes];
})
}
-(void)sentheart{
//发送心跳 和后台可以约定发送什么内容 一般可以调用ping 这里发送data
[self sendData:@"heart"];
}
#define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self
- (void)sendData:(id)data {
NSLog(@"socketSendData --------------- %@",data);
WeakSelf(ws);
dispatch_queue_t queue = dispatch_queue_create("giga", NULL);
dispatch_async(queue, ^{
if (weakSelf.socket != nil) {
// 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
if (weakSelf.socket.readyState == SR_OPEN) {
[weakSelf.socket send:data]; // 发送数据
} else if (weakSelf.socket.readyState == SR_CONNECTING) {
NSLog(@"正在连接中,重连后其他方法会去自动同步数据");
// 每隔2秒检测一次 socket.readyState 状态,检测 10 次左右
// 只要有一次状态是 SR_OPEN 的就调用 [ws.socket send:data] 发送数据
// 如果 10 次都还是没连上的,那这个发送请求就丢失了,这种情况是服务器的问题了,小概率的
//重连
[self reConnect];
} else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
// websocket 断开了,调用 reConnect 方法重连
NSLog(@"重连");
[self reConnect];
}
} else {
NSLog(@"没网络,发送失败,一旦断网 socket 会被我设置 nil 的");
}
});
}
//pingPong
- (void)ping{
if (self.socket.readyState == SR_OPEN) {
[self.socket sendPing:nil];
}
}
#pragma mark - socket delegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket{
//每次正常连接的时候清零重连时间
reConnectTime = 0;
//开启心跳
[self initHeartBeat];
if (webSocket == self.socket) {
NSLog(@"************************** socket 连接成功************************** ");
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidOpenNote object:nil];
}
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error
{
if (webSocket == self.socket) {
NSLog(@"************************** socket 连接失败************************** ");
NSLog(@"err:%@",error);
_socket = nil;
//连接失败就重连
[self reConnect];
}
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{
if (webSocket == self.socket) {
NSLog(@"************************** socket连接断开************************** ");
NSLog(@"被关闭连接code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
[self srWebSocketClose];
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidCloseNote object:nil];
}
}
/*该函数是接收服务器发送的pong消息其中最后一个是接受pong消息的
在这里就要提一下心跳包,一般情况下建立长连接都会建立一个心跳包,
用于每隔一段时间通知一次服务端客户端还是在线这个心跳包其实就是一个ping消息
我的理解就是建立一个定时器每隔十秒或者十五秒向服务端发送一个ping消息这个消息可是是空的
*/
-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
NSLog(@"pongPayload===%@",reply);
}
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
if (webSocket == self.socket) {
NSLog(@"************************** socket收到数据了************************** ");
NSLog(@"我这后台约定的 message 是 json 格式数据收到数据,就按格式解析吧,然后把数据发给调用层");
NSLog(@"message:%@",message);
[[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];
}
}
#pragma mark methods
//重连机制
-(void)reConnect{
[self srWebSocketClose];
//超过一分钟就不再重连 所以只会重连5次 2^5 = 64
if (reConnectTime > 64) {
return;
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.socket = nil;
[self srWebSocketOpenWithURLString:self.urlString];
NSLog(@"重连");
});
//重连时间2的指数级增长
if (reConnectTime ==0 ) {
reConnectTime = 2;
}else{
reConnectTime *=2;
}
}
- (SRReadyState)socketReadyState{
return self.socket.readyState;
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end