• iOS ws服务连接


    参照一篇对SRWebSocket封装的文章,具体链接找不到了,文件名是SocketRocketUtility。

    SDSocketRocketUtility.h

    #import "SDSocketRocketUtility.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           = @"kWebSocketDidOpenNote";
    NSString * const kWebSocketDidCloseNote          = @"kWebSocketDidCloseNote";
    NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote";
    
    @interface SDSocketRocketUtility()<SRWebSocketDelegate>
    {
        int _index;
        NSTimer * heartBeat;
        NSTimeInterval reConnectTime;
    }
    
    @property (nonatomic,strong) SRWebSocket *socket;
    
    @property (nonatomic,copy) NSString *urlString;
    
    @end

    SDSocketRocketUtility.m

    //
    //  SocketRocketUtility.m
    //  SUN
    //
    //  Created by 孙俊 on 17/2/16.
    //  Copyright © 2017年 SUN. All rights reserved.
    //
    
    #import "SDSocketRocketUtility.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           = @"kWebSocketDidOpenNote";
    NSString * const kWebSocketDidCloseNote          = @"kWebSocketDidCloseNote";
    NSString * const kWebSocketdidReceiveMessageNote = @"kWebSocketdidReceiveMessageNote";
    
    @interface SDSocketRocketUtility()<SRWebSocketDelegate>
    {
        int _index;
        NSTimer * heartBeat;
        NSTimeInterval reConnectTime;
    }
    
    @property (nonatomic,strong) SRWebSocket *socket;
    
    @property (nonatomic,copy) NSString *urlString;
    
    @end
    
    @implementation SDSocketRocketUtility
    
    + (SDSocketRocketUtility *)instance {
        static SDSocketRocketUtility *Instance = nil;
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            Instance = [[SDSocketRocketUtility alloc] init];
        });
        return Instance;
    }
    
    #pragma mark - **************** public methods
    -(void)SRWebSocketOpenWithURLString:(NSString *)urlString {
        
        //如果是同一个url return
        if (self.socket) {
            return;
        }
        
        if (!urlString) {
            return;
        }
        
        self.urlString = urlString;
        
        self.socket = [[SRWebSocket alloc] initWithURLRequest:
                       [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
        
        NSLog(@"请求的websocket地址:%@",self.socket.url.absoluteString);
        
        //SRWebSocketDelegate 协议
        self.socket.delegate = self;
        
        //开始连接
        [self.socket open];
    }
    
    - (void)SRWebSocketClose {
        if (self.socket){
            [self.socket close];
            self.socket = nil;
            //断开连接时销毁心跳
            [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 开启状态才能调 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 的");
                NSLog(@"其实最好是发送前判断一下网络状态比较好,我写的有点晦涩,socket==nil来表示断网");
            }
        });
    }
    
    #pragma mark - **************** private mothodes
    //重连机制
    - (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;
        }
    }
    
    
    //取消心跳
    - (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];
            //心跳设置10s
            heartBeat = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(sentheart) userInfo:nil repeats:YES];
            //和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
            [[NSRunLoop currentRunLoop] addTimer:heartBeat forMode:NSRunLoopCommonModes];
        })
    }
    
    - (void)sentheart {
        //发送心跳 和后台可以约定发送什么内容  一般可以调用ping  我这里根据后台的要求 发送了data给他
        [self sendData:@"heart"];
    }
    
    //pingPong
    - (void)ping {
        if (self.socket.readyState == SR_OPEN) {
            [self.socket sendPing:nil];
        }
    }
    
    #pragma mark - **************** SRWebSocketDelegate
    - (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 连接失败************************** ");
            _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(@"reply===%@",reply);
    }
    
    - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message  {
        
        if (webSocket == self.socket) {
            NSLog(@"************************** socket收到数据了************************** ");
            NSLog(@"我这后台约定的 message 是 json 格式数据收到数据,就按格式解析吧,然后把数据发给调用层");
            NSLog(@"message:%@",message);
            if (message != nil) {
                //if ([message containsString:@"ip"]) {
            
                   // NSData * clientData = [message dataUsingEncoding:NSUTF8StringEncoding];
                    //if (clientData != nil) {
                      //  NSDictionary * clientIPDict = [NSJSONSerialization JSONObjectWithData:clientData options:NSJSONReadingMutableContainers error:nil];
                       // _clientIP = clientIPDict[@"ip"];
                   // }
               // }
                
                [[NSNotificationCenter defaultCenter] postNotificationName:kWebSocketdidReceiveMessageNote object:message];
    
            }
        }
    }
    
    #pragma mark - **************** setter getter
    - (SRReadyState)socketReadyState {
        return self.socket.readyState;
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    /**
     *判断一个字符串是否是一个IP地址
     **/
    - (BOOL)isValidIP:(NSString *)ip {
        NSString  *isIP = @"^([1-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))(\.([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))){3}$";
        NSRegularExpression *regular = [[NSRegularExpression alloc] initWithPattern:isIP options:0 error:nil];
        NSArray *results = [regular matchesInString:ip options:0 range:NSMakeRange(0, ip.length)];
        return results.count > 0;
    }
    
    @end

    导入第三方SRWebSocket

    使用方法在使用的视图控制器中导入SDSocketRocketUtility.h文件

    连接

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidOpen) name:kWebSocketDidOpenNote object:nil];

     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(SRWebSocketDidReceiveMsg:) name:kWebSocketdidReceiveMessageNote object:nil];

    [[SDSocketRocketUtility instance] SRWebSocketOpenWithURLString:@"连接地址"];

    - (void)SRWebSocketDidOpen{

        NSLog(@"开启成功");

        //在成功后需要做的操作

    }

    - (void)SRWebSocketDidReceiveMsg:(NSNotification*)notification{

        //收到服务端发送过来的消息

     }

    关闭

    [[SDSocketRocketUtility instance] SRWebSocketClose];

  • 相关阅读:
    Extjs4单选按钮
    下拉框comboxBox本地数据源示例
    处理您的请求时发生异常。此外,对第一个异常执行自定义错误页时发生另一异常。已终止请
    hoj1568
    hoj3434
    poj2924
    poj2909
    hoj3433
    理解Python闭包,这应该是最好的例子
    Spring 学习笔记
  • 原文地址:https://www.cnblogs.com/lulushen/p/11435635.html
Copyright © 2020-2023  润新知