搜索
简帛阁>技术文章>iOS websocket

iOS websocket

iOS websocket

最近在开发一个直播应用,需要用到弹幕功能,后台说要用websocket来实现,所以学习了一下

一、 RocketSocket
搜索了一下发现,用的最多的还是Facebook的RocketSocket库,虽然已经停止维护了,但是还能使用。

  1. 创建socket

    - (SRWebSocket *)webSocket {
        if (!_webSocket) {
            NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:SOCKET_SERVER_URL]];
            [request setValue:[NSString getAccessTokenString] forHTTPHeaderField:@"Authorization"];
            _webSocket = [[SRWebSocket alloc] initWithURLRequest:request];
            _webSocket.delegate = self;
        }
        return _webSocket;
    }
    
  2. 打开socket,开始连接

    - (void)openWebSocket {
        if (self.webSocket.readyState == SR_OPEN) {
            return;
        }
        [self.webSocket open];
    }
    
  3. 监听消息

    - (void)subscribeNewMessage {
        NSString *sendString = [NSString stringWithFormat:@"SUBSCRIBE\ndestination: %@\nid: %@\n\n", SOCKET_SEND_URL(@(self.channelId)), @"sub-0"];
        NSLog(@"%@", sendString);
        NSData *data = [sendString dataUsingEncoding:NSUTF8StringEncoding];
        [self.webSocket send:data];
    }
    
  4. 发送消息

    - (void)sendMessage:(NSDictionary *)message {
        if (self.webSocket) {
            if (self.webSocket.readyState == SR_OPEN) {
                NSString *sendString = [NSString stringWithFormat:@"SEND\nAuthorization: %@\ndestination: %@\n\n%@", [NSString getAccessTokenString], SOCKET_SEND_URL(message[@"accountId"]), [NSString getJsonStringOfDictionary:message]];
                NSLog(@"sendMessage :%@", sendString);
                NSData *data = [sendString dataUsingEncoding:NSUTF8StringEncoding];
                [self.webSocket send:data];
            }
        } else {
            [self openWebSocket];
        }
    }
    
  5. SRWebSocketDelegate, 代理事件

    #pragma mark - SRWebSocketDelegate
    
    - (void)webSocketDidOpen:(SRWebSocket *)webSocket {
        NSLog(@"webSocketDidOpen");
        if ([webSocket isEqual:self.webSocket]) {
            self.reopenCount = 0;
    //        [self startHeartBeat];
    //        [self subscribeNewMessage];
        }
    }
    
    - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
        NSLog(@"didFailWithError: %@", error);
        if ([webSocket isEqual:self.webSocket]) {
            [self reopenWebSocket];
        }
    }
    
    - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
        NSLog(@"didCloseWithCode: %ld, %@, %d", (long)code, reason, wasClean);
        if ([webSocket isEqual:self.webSocket]) {
            [self closeWebSocket];
        }
    }
    
    - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload {
        NSLog(@"didReceivePong: %@", pongPayload);
        if ([webSocket isEqual:self.webSocket]) {
    
        }
    }
    
    - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
        NSLog(@"didReceiveMessage: %@", message);
        if ([webSocket isEqual:self.webSocket]) {
            if (self.receiveMessage) {
                self.receiveMessage(message);
            }
        }
    }
    
  6. 心跳和重连
    因为封装的比较简单,代码看起来很明了,但是有些地方需要自己完善。我这里主要加了心跳和重连机制。

    - (NSTimer *)heartBeatTimer {
        if (!_heartBeatTimer) {
            _heartBeatTimer = [NSTimer timerWithTimeInterval:heartBeatTimeInterval target:self selector:@selector(sendPing) userInfo:nil repeats:YES];
        }
        return _heartBeatTimer;
    }
    
    - (void)startHeartBeat {
        if (!_heartBeatTimer) {
            [[NSRunLoop currentRunLoop] addTimer:self.heartBeatTimer forMode:NSRunLoopCommonModes];
    //        [[NSRunLoop currentRunLoop] run];//子线程需要手动启动runloop
        }
    }
    
    - (void)stopHeartBeat {
        if (_heartBeatTimer) {
            [self.heartBeatTimer invalidate];
            self.heartBeatTimer = nil;
        }
    }
    
    - (void)sendPing {
        if (self.webSocket.readyState == SR_OPEN) {
            [self.webSocket sendPing:nil];
        }
    }
    
    - (void)reopenWebSocket {
        [self closeWebSocket];
        if (self.reopenCount >= maxReopenCount) {
            if (self.reconnectFailed) {
                self.reconnectFailed();
            }
            return;
        }
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
            [self openWebSocket];
            self.reopenCount ++;
        });
    }
    

二、 WebsocketStompKit
用rocketsocket写完之后,开始和后端联调,发现连接成功之后后端根本发现不了,连log都没有。对比着js中的方法,发现有用到Stomp,看源码挺简单的,只是把数据封装了一层,所以我就照着写了,在调试的时候还是不行。然后就去网上搜索iOS支持stomp协议的websocket库,就找到了这个WebsocketStompKit

这个库封装的更好一些,用起来很方便。

  1. 创建client

    - (STOMPClient *)webSocket {
        if (!_webSocket) {
            NSURL *url = [NSURL URLWithString:SOCKET_SERVER_URL];
            NSDictionary *headers = @{
                @"Authorization": [NSString getAccessTokenString],
            };
            _webSocket = [[STOMPClient alloc] initWithURL:url webSocketHeaders:headers useHeartbeat:YES];
            _webSocket.delegate = self;
        }
        return _webSocket;
    }
    
  2. 打开socket,开始连接

    - (void)openWebSocket {
        NSDictionary *headers = @{
            @"Authorization": [NSString getAccessTokenString],
        };
        [self.webSocket connectWithHeaders:headers completionHandler:^(STOMPFrame *connectedFrame, NSError *error) {
            NSLog(@"connectWithHeaders: %@---%@", connectedFrame, error);
            if (error) {//连接失败提示
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                });
            } else {//连接成功订阅消息
                [self subscribeNewMessage];
            }
        }];
    }
    
  3. 订阅消息

    - (void)subscribeNewMessage {
        [self.webSocket subscribeTo:[NSString stringWithFormat:@"/topic/222.%lld", self.channelId] headers:@{} messageHandler:^(STOMPMessage *message) {
            NSLog(@"receive message: %@", message);
            if (self.receiveMessage) {
                self.receiveMessage([NSString getDictionaryFromJsonString:message.body]);
            }
        }];
    }
    
  4. 发送消息

    - (void)sendMessage:(NSDictionary *)message {
        NSDictionary *headers = @{
            @"Authorization": [NSString getAccessTokenString],
        };
        [self.webSocket sendTo:SOCKET_SEND_URL(message[@"accountId"]) headers:headers body:[NSString getJsonStringOfDictionary:message]];
    }
    

使用过程中又崩溃的情况,我这里修改了几个地方,详情请见我的Fork

比较上面的两种库,回调方式分别用来delegate和block,看起来使用delegate的方式容易对整个流程有把控,每一步都有对应的方法,写起来稍微麻烦一些,但是容易理解;使用block的方式写起来很简单,但是理解起来稍显困难,不过更容易集中注意力到事件及回调上,其他过程可忽略。我倾向于前者。

参考:
iOS WebSocket长连接
WebSocket Demo
WebsocketStompKit使用

iOSwebsocket最近在开发一个直播应用,需要用到弹幕功能,后台说要用websocket来实现,所以学习了一下一、RocketSocket搜索了一下发现,用的最多的还是Facebook的Roc
WebSocketWebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但是
公司项目需要开发一个类似QQ、微信的即时IM聊天功能,做到实时监控消息,需要用的技术是websocket。概述WebSocket:11为什么我们需要WebSocket这样的实时的通信协议?WebSoc
公司项目需要开发一个类似QQ、微信的即时IM聊天功能,做到实时监控消息,需要用的技术是websocket,今天整理下语言聊天这块;其实语言聊天,包含两部分,录音和音乐播放,关于简单语言聊天功能如下图
一、背景公司最近准备将一套产品放到Andriod和IOS上面去,为了统一应用的开发方式,决定用各平台APP嵌套一个HTML5浏览器来实现,其中数据通信,准备使用WebSocket的方式。于是,我开始在
承接上篇(解决sockjs、stomp在uniapp端使用的坑)第3个问题:在安卓app上是可以正常建立链接的,但是在iosapp上却不行。虽然不行,但还是需要解决。起初查百度,很多人说是nginx配
https://xiaozhuanlancom/topic/9273604158先来张渲染的流程图:这张图其实有很多误导,我的更改如下:可以看到整个流程是一个pipeline(一次pipeline要跨
iOS国际化的大致步骤如下:1在最新的Xcode中没有InfoPliststringsLocalizationstrings文件,所以我们要自己建立这两个文件2选择刚创建的Localizations
1block可以用来保存一段代码,或者用来封装一段代码。--->代码段,代码块。2block的标志是^3block跟函数很像可以保存代码;可以有返回值;也可以有行参;调用方式一样;4定义一个bl
1假设有三个对象,一个父类的父类,一个父类和一个子类。父类的父类持有父类的引用(retain),父类持有子类的引用(retain),子类持有父类的引用(retain)。父类的父类释放(release)