【iOS】—— 实现WebSocket发送消息(SocketRocket第三方库的使用和解析)

这篇具有很好参考价值的文章主要介绍了【iOS】—— 实现WebSocket发送消息(SocketRocket第三方库的使用和解析)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


偶然之间了解到了利用WebSocket实现后端和前端的相互发送消息,就查了查在iOS里这个东西该怎么写,用舍友写的接口简单实现了两个用户的通信。

WebSocket

  • WebSocket 是一种在 Web 应用程序中实现双向通信的协议。它允许客户端和服务器之间建立一个持久性的连接,以便可以在任何时间点进行双向通信。
  • 传统的 HTTP 请求-响应模式只支持客户端发起请求,服务器做出响应。也就是说,当一个页面被加载时,它通常会发起一个 HTTP 请求获取某些资源,例如 HTMLCSSJavaScript 文件等。一旦这些资源被发送到浏览器,连接就被关闭了,除非有其他请求发送。
  • 但是,对于某些 Web 应用程序,需要更加实时的交互。这就是 WebSocket 所提供的优势:它们允许客户端和服务器之间保持开放的连接,使得双方可以随时发送消息。
  • WebSocket 使用 HTTP 协议进行握手,然后升级到 WebSocket协议。一旦这个升级完成,客户端和服务器将使用 WebSocket 协议进行通信。WebSocket 通过使用标准的 TCP/IP 套接字来实现,在网络上发送和接收数据。
  • WebSocket 在很多场景下都非常实用,比如在线游戏、实时聊天、股票行情推送等,可以大大提高应用程序的交互效果和用户体验。

WebSocket特点

1. 建立在 TCP 协议之上,服务器端的实现比较容易。
2. 与 HTTP 协议有着良好的兼容性。默认端口也是80443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
3. 数据格式比较轻量,性能开销小,通信高效。
4. 可以发送文本,也可以发送二进制数据。
5. 没有同源限制,客户端可以与任意服务器通信。
6. 协议标识符是ws(如果加密,则为wss),服务器网址就是URL。`ws://example.com:80/some/path`
7. 全双工通信。

SocketRocket

使用方法和之前的第三方库一样,利用cocoapods导库,具体操作就不详写了,在之前的博客里也写到过。

导入头文件设置代理

在需要用的地方导入#import <SocketRocket/SRWebSocket.h>
并遵循代理 SRWebSocketDelegate
声明一个属性 SRWebSocket *webSocket;

SRWebSocket的初始化和建立连接

    self.socket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://101.43.193.62:9090/ws?id=%d", self.senderSubscriberNumber]]]];
    self.socket.delegate = self;
    NSLog(@"Opening Connection...");
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_async(queue, ^{
        [self.socket open];
    });

在这里看到,我为什么把 [self.socket open];这句代码放在了一个异步并发队列里,在之前我直接在主线程调用open方法,编译器给我报了一堆奇怪的东西,查找之后告诉我是线程优先级倒置导致的:
在iOS中,每个线程都有一个与之关联的Quality of Service(QoS)级别,用于指定它的相对优先级。 QOS_CLASS_USER_INTERACTIVE 表示线程需要立即响应并具有最高优先级,而 QOS_CLASS_DEFAULT 表示线程具有默认优先级。

这个问题的原因应该是这个第三方库底层的原因,我看了一些源码,发现调用了很多GCD的东西,暂时还没发现原因。

SRWebSocketDelegate 代理方法实现

//socket 连接成功
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
    NSLog(@"Websocket Connected");
}

//socket 连接失败
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
    NSLog(@":( Websocket Failed With Error %@", error);
    self.socket = nil;
    // 断开连接后每过1s重新建立一次连接
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self setSocket];
    });
}

//socket连接断开
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
    NSLog(@"WebSocket closed");
    self.socket = nil;
}

//收到消息
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
    NSLog(@"Received \"%@\"", message);
}

四个最常用方法分别表示,连接成功,连接失败,连接断开,和收到消息,我们在每个协议函数中做自己该做的事即可。

后端发给我的信息是这样的:
ios websocket,ios,websocket,网络协议
在接收到消息时本想用字典存储,转化的时候刚开始一直有问题,试了好多次才转化成功。

    NSString *messageString = (NSString *)message;
    NSData *messageData = [messageString dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *messageDictionary = [NSJSONSerialization JSONObjectWithData:messageData options:kNilOptions error:nil];

加上简单UI实现两个用户之间简单通信

ios websocket,ios,websocket,网络协议

ios websocket,ios,websocket,网络协议

浅看了一点点源码(理解的不深)

1.基本初始化方法
- (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates

底层调用了_SR_commonInit方法

//初始化
- (void)_SR_commonInit;
{
    //得到url schem小写
    NSString *scheme = _url.scheme.lowercaseString;
        //如果不是这几种,则断言错误
    assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);

    _readyState = SR_CONNECTING;
    _webSocketVersion = 13;
     //初始化工作的队列,串行
    _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    
    //给队列设置一个标识,标识为指向自己的,上下文对象为这个队列
    dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL);
    
    //设置代理queue为主队列
    _delegateDispatchQueue = dispatch_get_main_queue();
    
    //retain主队列?
    sr_dispatch_retain(_delegateDispatchQueue);
    
    //读Buffer
    _readBuffer = [[NSMutableData alloc] init];
    //输出Buffer
    _outputBuffer = [[NSMutableData alloc] init];
    //当前数据帧
    _currentFrameData = [[NSMutableData alloc] init];
    //消费者数据帧的对象
    _consumers = [[NSMutableArray alloc] init];
    
    _consumerPool = [[SRIOConsumerPool alloc] init];
    //注册的runloop
    _scheduledRunloops = [[NSMutableSet alloc] init];
    ....省略了一部分代码
}

整个过程:

  • 判断协议类型
  • 初始化了_workQueue,这个GCD队列用来处理主要的业务逻辑,包括处理错误、发送内容、关闭连接等。
  • 初始化_delegateDispatchQueue,这个队列是用来向外发送通知的。我们可以通过- (void)setDelegateOperationQueue:(NSOperationQueue*) queue- (void)setDelegateDispatchQueue:(dispatch_queue_t) queue来自定义这个队列。

2.创建输入输出流

//初始化流
- (void)_initializeStreams;
{
    //断言 port值小于UINT32_MAX
    assert(_url.port.unsignedIntValue <= UINT32_MAX);
    //拿到端口
    uint32_t port = _url.port.unsignedIntValue;
    //如果端口号为0,给个默认值,http 80 https 443;
    if (port == 0) {
        if (!_secure) {
            port = 80;
        } else {
            port = 443;
        }
    }
    NSString *host = _url.host;
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    //用host创建读写stream,Host和port就绑定在一起了
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
    
    //绑定生命周期给ARC  _outputStream = __bridge transfer
    _outputStream = CFBridgingRelease(writeStream);
    _inputStream = CFBridgingRelease(readStream);
    
    //代理设为自己
    _inputStream.delegate = self;
    _outputStream.delegate = self;
}

3.open方法

//开始连接
- (void)open;
{
    assert(_url);
    //如果状态是正在连接,直接断言出错
    NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSocket more than once");

    //自己持有自己
    _selfRetain = self;
    //判断超时时长
    if (_urlRequest.timeoutInterval > 0)
    {
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, _urlRequest.timeoutInterval * NSEC_PER_SEC);
        //在超时时间执行
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            //如果还在连接,报错
            if (self.readyState == SR_CONNECTING)
                [self _failWithError:[NSError errorWithDomain:@"com.squareup.SocketRocket" code:504 userInfo:@{NSLocalizedDescriptionKey: @"Timeout Connecting to Server"}]];
        });
    }
    //开始建立连接
    [self openConnection];
}
//开始连接
- (void)openConnection;
{
    //更新安全、流配置
    [self _updateSecureStreamOptions];
    
    //判断有没有runloop
    if (!_scheduledRunloops.count) {
        //SR_networkRunLoop会创建一个带runloop的常驻线程,模式为NSDefaultRunLoopMode。
        [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    //开启输入输出流
    [_outputStream open];
    [_inputStream open];
}

- (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
{
    [_outputStream scheduleInRunLoop:aRunLoop forMode:mode];
    [_inputStream scheduleInRunLoop:aRunLoop forMode:mode];
    
    //添加到集合里,数组
    [_scheduledRunloops addObject:@[aRunLoop, mode]];
}

再往后就没看了,浅看了这两三个方法,后面还会调的方法:文章来源地址https://www.toymoban.com/news/detail-613725.html

  • didConnect
    • 构建HTTP Header
    • 发送HTTP Header
    • 注册一个接收服务器返回Header信息的监听,并在回调内进行相应处理
  • - (void)safeHandleEvent:(NSStreamEvent)eventCode stream:(NSStream *)aStream
    • 这是NSStream的回调方法,输入和输出流的共同回调
    • NSStreamEventOpenCompleted 连接打开;NSStreamEventHasBytesAvailable 可读取;NSStreamEventHasSpaceAvailable 可写入数据
    • NSStreamEventOpenCompleted里面的[self _pumpScanner];用来触发第5条中的3,来处理服务器返回的握手Header信息

到了这里,关于【iOS】—— 实现WebSocket发送消息(SocketRocket第三方库的使用和解析)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring定时任务+webSocket实现定时给指定用户发送消息

    生命无罪,健康万岁,我是laity。 我曾七次鄙视自己的灵魂: 第一次,当它本可进取时,却故作谦卑; 第二次,当它在空虚时,用爱欲来填充; 第三次,在困难和容易之间,它选择了容易; 第四次,它犯了错,却借由别人也会犯错来宽慰自己; 第五次,它自由软弱,却把

    2024年02月07日
    浏览(33)
  • 如何在前端实现WebSocket发送和接收TCP消息(多线程模式)

    当在前端实现WebSocket发送和接收TCP消息时,可以使用以下步骤来实现多线程模式。本文将详细介绍如何在前端实现WebSocket发送和接收TCP消息,并解释使用到的相关函数及原理。 在前端实现WebSocket发送和接收TCP消息的第一步是创建一个WebSocket连接。我们可以使用浏览器提供的

    2024年02月12日
    浏览(29)
  • 如何在前端实现WebSocket发送和接收UDP消息(多线程模式)

    本文将继续介绍如何在前端应用中利用WebSocket技术发送和接收UDP消息,并引入多线程模式来提高发送效率和性能。我们将使用JavaScript语言来编写代码,并结合WebSocket API、UDP数据包、Web Workers和UDP消息监听器来实现这一功能。 首先,我们需要在前端应用中建立一个WebSocket连接

    2024年02月12日
    浏览(28)
  • java后端使用websocket实现与客户端之间接收及发送消息

    客户端请求websocket接口,连接通道=》我这边业务成功客户端发消息=》客户端自动刷新。 接口:ws://localhost:8080/websocket/xx 经测试,成功 如果是线上服务器连接,则需要在nginx里配置websocket相关内容,再重启nginx,代码如下 本地连接的时候用的是ws://,因为是http链接,但是如果是

    2024年02月16日
    浏览(29)
  • SSE与WebSocket分别实现服务器发送消息通知(Golang、Gin)

    服务端推送,也称为消息推送或通知推送,是一种允许应用服务器主动将信息发送到客户端的能力,为客户端提供了实时的信息更新和通知,增强了用户体验。 服务端推送的背景与需求主要基于以下几个诉求: 实时通知:在很多情况下,用户期望实时接收到应用的通知,如

    2024年02月03日
    浏览(35)
  • 泛微OA获取流程附件地址,发送邮件或上传到第三方系统中

    在泛微的流程开发当中,有些流程的数据、附件要上传到其它平台,与其它平台相互集成对接, 一般是在写Action事件时,就会自动把数据及附件上传到其它平台,以下内容是经过正常使用获取流程附件的业务 示例:泛微OA中流程附件

    2024年02月15日
    浏览(47)
  • iOS 开发-编译第三方库 openssl及curl

    iOS编译库需要三个架构,arm64,arm64e,x86_64,其中x86_64为模拟器所需 iOS编译库需要下载xcode及对应的command line tool(执行命令时可以自动下载),下载失败需要去官网搜索下载 参考iOS如何编译OpenSSL静态库(openssl版本:1.1.1b) 执行配置命令(路径修改为要生成的目标架构文件夹路径

    2024年02月13日
    浏览(28)
  • 【iOS】—— swift基础语法及一些第三方库使用

    只能赋值一次 它的值不要求在编译时期确定,但使用之前必须赋值一次 可以被赋值多次 跟常量一样,在使用之前必须给他赋值,否则编译器会报错 这个第三方库和Masonry的作用和用法都很相似 其中这块我们看到和oc不同的是 Int(SIZE_HEIGHT) 这块有个强制类型转化,这块原因是

    2024年02月08日
    浏览(37)
  • 138. 第三方系统或者工具通过 HTTP 请求发送给 ABAP 系统的数据,应该如何解析

    本教程第 37 篇文章,我们介绍了如何在 SAP ABAP 系统 SICF 事务码 里,开发一段 ABAP 代码,用来响应通过浏览器或者第三方工具,比如 curl,Postman 发起的 HTTP 请求。 31. 如何让 ABAP 服务器能够响应通过浏览器发起的自定义 HTTP 请求 在实际的 ABAP 集成项目中,这种方式非常使用。

    2024年03月21日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包