iOS webSocket之SocketRocket

在做項(xiàng)目過(guò)程中有個(gè)需求,需要在請(qǐng)求接口后建立長(zhǎng)連接,并且一直接收傳過(guò)來(lái)的數(shù)據(jù),我是用的facebook的SocketRocket框架實(shí)現(xiàn)的,可以用cocoapods導(dǎo)入。

Demo下載地址:https://github.com/huangqizai/webSocket

不多說(shuō)上代碼:
以下代碼最好在單利里面實(shí)現(xiàn)...

#pragma mark 輸入接口地址,開(kāi)始連接
-(void)SRWebSocketOpenWithURLString:(NSString *)urlString {
    
    //如果是同一個(gè)url return
    if (self.socket) {
        return;
    }
    
    if (!urlString) {
        return;
    }
    
    self.urlString = urlString;
    
    self.socket = [[SRWebSocket alloc] initWithURLRequest:
                   [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
    
    NSLog(@"請(qǐng)求的websocket地址:%@",self.socket.url.absoluteString);

    self.socket.delegate = self;   //SRWebSocketDelegate 協(xié)議
    
    [self.socket open];     //開(kāi)始連接
}

-(void)SRWebSocketClose{
    if (self.socket){
        [self.socket close];
        self.socket = nil;
        //斷開(kāi)連接時(shí)銷(xiāo)毀心跳
        [self destoryHeartBeat];
    }
}

#define WeakSelf(ws) __weak __typeof(&*self)weakSelf = self
- (void)sendData:(id)data {
    NSLog(@"socketSendData --------------- %@",data);
    
    WeakSelf(ws);
    dispatch_queue_t queue =  dispatch_queue_create("zy", NULL);
    
    dispatch_async(queue, ^{
        if (weakSelf.socket != nil) {
            // 只有 SR_OPEN 開(kāi)啟狀態(tài)才能調(diào) send 方法啊,不然要崩
            if (weakSelf.socket.readyState == SR_OPEN) {
                [weakSelf.socket send:data];    // 發(fā)送數(shù)據(jù)
                
            } else if (weakSelf.socket.readyState == SR_CONNECTING) {
                NSLog(@"正在連接中,重連后其他方法會(huì)去自動(dòng)同步數(shù)據(jù)");
                // 每隔2秒檢測(cè)一次 socket.readyState 狀態(tài),檢測(cè) 10 次左右
                // 只要有一次狀態(tài)是 SR_OPEN 的就調(diào)用 [ws.socket send:data] 發(fā)送數(shù)據(jù)
                // 如果 10 次都還是沒(méi)連上的,那這個(gè)發(fā)送請(qǐng)求就丟失了,這種情況是服務(wù)器的問(wèn)題了,小概率的
                // 代碼有點(diǎn)長(zhǎng),我就寫(xiě)個(gè)邏輯在這里好了
                [self reConnect];
                
            } else if (weakSelf.socket.readyState == SR_CLOSING || weakSelf.socket.readyState == SR_CLOSED) {
                // websocket 斷開(kāi)了,調(diào)用 reConnect 方法重連
                
                NSLog(@"重連");
                
                [self reConnect];
            }
        } else {
            NSLog(@"沒(méi)網(wǎng)絡(luò),發(fā)送失敗,一旦斷網(wǎng) socket 會(huì)被我設(shè)置 nil 的");
            NSLog(@"其實(shí)最好是發(fā)送前判斷一下網(wǎng)絡(luò)狀態(tài)比較好,我寫(xiě)的有點(diǎn)晦澀,socket==nil來(lái)表示斷網(wǎng)");
        }
    });
}

#pragma mark - **************** private mothodes
//重連機(jī)制
- (void)reConnect
{
    [self SRWebSocketClose];

    //超過(guò)一分鐘就不再重連 所以只會(huì)重連5次 2^5 = 64
    if (reConnectTime > 64) {
        //您的網(wǎng)絡(luò)狀況不是很好,請(qǐng)檢查網(wǎng)絡(luò)后重試
        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(@"重連");
    });
    
    //重連時(shí)間2的指數(shù)級(jí)增長(zhǎng)
    if (reConnectTime == 0) {
        reConnectTime = 2;
    }else{
        reConnectTime *= 2;
    }
}


//取消心跳
- (void)destoryHeartBeat
{
    dispatch_main_async_safe(^{
        if (heartBeat) {
            if ([heartBeat respondsToSelector:@selector(isValid)]){
                if ([heartBeat isValid]){
                    [heartBeat invalidate];
                    heartBeat = nil;
                }
            }
        }
    })
}

//初始化心跳
- (void)initHeartBeat
{
    dispatch_main_async_safe(^{
        [self destoryHeartBeat];
        //心跳設(shè)置為3分鐘,NAT超時(shí)一般為5分鐘
        heartBeat = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(sentheart) userInfo:nil repeats:YES];
        //和服務(wù)端約定好發(fā)送什么作為心跳標(biāo)識(shí),盡可能的減小心跳包大小
        [[NSRunLoop currentRunLoop] addTimer:heartBeat forMode:NSRunLoopCommonModes];
    })
}

-(void)sentheart{
    //發(fā)送心跳 和后臺(tái)可以約定發(fā)送什么內(nèi)容  一般可以調(diào)用ping  我這里根據(jù)后臺(tái)的要求 發(fā)送了data給他
    [self sendData:@"heart"];
}

//pingPong
- (void)ping{
    if (self.socket.readyState == SR_OPEN) {
        [self.socket sendPing:nil];
    }
}

#pragma mark - socket delegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    //每次正常連接的時(shí)候清零重連時(shí)間
    reConnectTime = 0;
    //開(kāi)啟心跳
    [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 連接失敗************************** ");
        _socket = nil;
        //連接失敗就重連
        [self reConnect];
    }
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
    
    if (webSocket == self.socket) {
        NSLog(@"************************** socket連接斷開(kāi)************************** ");
        NSLog(@"被關(guān)閉連接,code:%ld,reason:%@,wasClean:%d",(long)code,reason,wasClean);
        [self SRWebSocketClose];
        [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketDidCloseNote object:nil];
    }
}

/*該函數(shù)是接收服務(wù)器發(fā)送的pong消息,其中最后一個(gè)是接受pong消息的,
 在這里就要提一下心跳包,一般情況下建立長(zhǎng)連接都會(huì)建立一個(gè)心跳包,
 用于每隔一段時(shí)間通知一次服務(wù)端,客戶(hù)端還是在線,這個(gè)心跳包其實(shí)就是一個(gè)ping消息,
 我的理解就是建立一個(gè)定時(shí)器,每隔十秒或者十五秒向服務(wù)端發(fā)送一個(gè)ping消息,這個(gè)消息可是是空的
 */
-(void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
    NSString *reply = [[NSString alloc] initWithData:pongPayload encoding:NSUTF8StringEncoding];
    NSLog(@"reply===%@",reply);
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
    
    if (webSocket == self.socket) {
        NSLog(@"************************** socket收到數(shù)據(jù)了************************** ");
        NSLog(@"我這后臺(tái)約定的 message 是 json 格式數(shù)據(jù)收到數(shù)據(jù),就按格式解析吧,然后把數(shù)據(jù)發(fā)給調(diào)用層");
        NSLog(@"message:%@",message);
        
        [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];
    }
}

#pragma mark - **************** setter getter
- (SRReadyState)socketReadyState{
    return self.socket.readyState;
}

調(diào)用單利里面的webSocket實(shí)現(xiàn)長(zhǎng)連接并返回?cái)?shù)據(jù)

 [[SocketRocketUtility instance] SRWebSocketOpenWithURLString:@"http://172.24.120.26:8510/sync"];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidOpen) name:kWebSocketDidOpenNote object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidReceiveMsg:) name:kWebSocketdidReceiveMessageNote object:nil];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容