iOS使用webSocket通信

一、什么是webSocket

webSocket是HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。对大部分web开发者来说,上面这段描述有点枯燥,其实只要记住几点:

  1. WebSocket可以在浏览器里使用
  2. 支持双向通信
  3. 使用很简单

有哪些优点

说到优点,这里的对比参照物是HTTP协议,概括地说就是:支持双向通信,更灵活,更高效,可扩展性更好。

  1. 支持双向通信,实时性更强。
  2. 更好的二进制支持。
  3. 较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
  4. 支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)

二、iOS中如何使用webSocket

     webSocket 实现了服务端推机制(主动向客户端发送消息)。新的 web 浏览器全都支持 WebSocket,这使得它的使用超级简单。通过 WebSocket 能够打开持久连接,大部分网络都能轻松处理 WebSocket 连接。在 iOS 中使用 WebSocket 比较麻烦,你必须进行大量的设置,而且内置的 API 根本帮不上忙。这时 Starscream 出现了——这个小巧、易于使用的库让你所有的烦恼不翼而飞。 

1、根据url创建socket

            var request = URLRequest(url: URL(string: "url")!)request.timeoutInterval = 5//超时时间socket = WebSocket(request: request)socket.delegate = self//接收到消息走代理方法

2、发送消息

socket.write(string: sendStr)

3、接收消息,在代理方法中

func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {//接收到字符串消息}func websocketDidReceiveData(socket: WebSocketClient, data: Data) {printLog(“\(data)”)//接收到data消息}

三、常见问题

1、如何确保client向特定的client发送消息

发送消息时带着要发送给哪些client(唯一标识性数组)发送给cloud,cloud根据要发送给的client数组向相应的client发送消息

 func sendTextTo(deviceIDs: [String], text: String) {if socket == nil  {return}if socket.isConnected == false {return}let cmdMessage = DQMessage(Type: 1, MsgGID: UUID().uuidString, Receivers: deviceIDs, Content: text, Time: nil, Publisher: nil, axOrderIDs: nil)if let sendStr = DQMessage.toJsonString(messages: [cmdMessage]) {socket.write(string: sendStr)}}

2.如何保持链接

十分钟发送一次心跳包,app进入前台时,app断网重连时,app失去web连接时,重新连接


NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)do {try reachability.startNotifier()} catch {print("Unable to start notifier")}reachability.whenReachable = { [weak self] reachability inself?.reconnectTimes = 10if self?.socket == nil {return}self?.socket.connect()}if #available(iOS 10.0, *) {timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { timer inlet now = Date().timeIntervalSince1970let s = now - self.lastReceivedMessageTimeif s >= 600 && s <= 660  {self.sendHeartBeat()} else if s > 660 {self.reconnect()}}} else {timer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(handleTimer), userInfo: nil, repeats: true)}@objc func handleTimer(timer: Timer) {let now = Date().timeIntervalSince1970let s = now - self.lastReceivedMessageTimeif s >= 600 && s <= 660  {self.sendHeartBeat()} else if s > 660 {self.reconnect()}}@objc func appDidBecomeActive(_ application: UIApplication) {if self.socket == nil {return}if self.socket.isConnected == false && self.reachability.connection != .none {self.socket.connect()}}

3.如何保证消息送达

      client到cloud:client中维护一个message数据表(包括字段是否发送成功sent)cloud收到消息之后向client返回ack,client收到ack后将该条message标记为sent=1已发送,60秒client未收到ack,视为发送失败,重新发送,  cloud端message表中已经存在该条消息,则忽略,但是向客户端client发送ack

       cloud到clinet:client收到消息后向cloud返回ack,cloud收到ack标记消息为已发送成功, 60秒cloud未收到ack,视为发送失败,从新发送,client端message表中已经存在该条消息,则忽略,但是向客户端client发送ack

func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {printLog("Web Socket receive \(text)")lastReceivedMessageTime = Date().timeIntervalSince1970if text == "$" {printLog("Received Heart Beat!!!!")return}guard let messageArray = DQMessage.from(jsonData: text.data(using: .utf8)!) else { return }for message in messageArray {if message.Type == 99 { //ACKDBPool.write { db intry? db.execute("Update MessageRecord Set sent = 1 Where messageGID = '\(message.MsgGID)'")}continue}//收到消息后回复ACK,这样服务器会标记这条消息发送成功sendACK(message: message)//从收到的消息列表中对比msgid, 如果已经收到过,则忽略这条消息, 去重处理var shouldReturn = falseDBQueue.inDatabase { db indo {if let count = try Int.fetchOne(db, "Select count(*) from MessageRecord where messageGID = '\(message.MsgGID)'"), count > 0 {//数据库里有这条消息,说明已经收到过,忽略掉shouldReturn = true}} catch {Log.shareInstance.log(message: "读取数据库错误")printLog("读取数据库错误")self.createTable()}}if shouldReturn == true {return}//发完ACK将message存到数据库let aMessage = essageRecord()aMessage.messageGID = message.MsgGIDaMessage.type = message.TypeaMessage.time = message.TimeaMessage.publisher = message.Publisherif message.Type == 1 { //textguard let content = message.Content else { continue }aMessage.message = message.Contentif content.hasPrefix("cmd::") {let ar = content.components(separatedBy: "::")var para: String? = nilif ar.count == 3 {para = ar[2]}let cmdString = "\(ar[0])::\(ar[1])".lowercased()let command = AldeloCommand(rawValue: cmdString) ?? AldeloCommand.unknownif command == .clinePrint {if let ar = para?.components(separatedBy: ","), ar.count == 2 {if let orderID = Int64(ar[0]), orderID > 0 {gotPrintCommandBlock?([orderID],ar[1].boolValue(),true, message)delegate?.receivedPrintCommand(axOrderIDs: [orderID], packingPrint: ar[1].boolValue(), isClientWebSocket: true)}}} else {gotCommandBlock?(command,para, message)delegate?.receivedCommand(cmd: command,parameter: para,  message: message)}} else {gotMessageBlock?(message)delegate?.receivedMessage(message: message)}} else if message.Type == 2 { //printguard let orderIDs = message.axOrderIDs else { return }aMessage.message = "\(orderIDs)"gotPrintCommandBlock?(orderIDs,true,false,message)delegate?.receivedPrintCommand(axOrderIDs: orderIDs, packingPrint: true, isClientWebSocket: false)} else if message.Type == 3 { //QR paymentguard let content = message.Content else { continue }aMessage.message = contentgotQRPaymentBlock?(content)delegate?.receivedQRPayment(content: content)} else if message.Type == 4 { //cloud 强制反激活printLog("cloud 强制反激活 .....")}DBPool.write { db intry? aMessage.insert(db)}}}

参考 Starscream 在 GitHub 上的项目主页 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/7836.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

发电机组远程管理,提升管控力,降低运维成本

发电机组是指发电机发动机以及控制系统的总称&#xff0c;用来把发动机提供的动能转化为电能。它通常由动力系统、控制系统、消音系统、减震系统、排气系统组成。发电机组远程管理系统利用物联网技术与PLC远程控制模块集成解决方案&#xff0c;在提高发电机组的运行效率、降低运…

【计算机科学速成课】笔记三——操作系统

文章目录 18.操作系统问题引出——批处理设备驱动程序多任务处理虚拟内存内存保护Unix 18.操作系统 问题引出—— Computers in the 1940s and early 50s ran one program at a time. 1940,1950 年代的电脑&#xff0c;每次只能运行一个程序 A programmer would write one at…

Vue基础——Mixin(混入)

在Vue中&#xff0c;Mixin&#xff08;混入&#xff09;是一种可以重复使用的代码片段&#xff0c;可以包含组件选项&#xff08;如data、methods、computed等&#xff09;以及生命周期钩子函数。Mixin可以在多个组件中共享相同的逻辑或功能&#xff0c;从而实现代码的复用和组…

Django框架四-项目

一、项目准备 1.流程与人员 2.需求分析 项目主要页面 归纳项目主要模块 3.架构设计 项目开发模式 项目架构设计

【C++STL详解(八)】--------stack和queue的模拟实现

目录 前言 一、stack模拟实现 二、queue的模拟实现 前言 前面也介绍了stack和queue的常见接口&#xff0c;我们也知道stack和queue实际上是一种容器适配器&#xff0c;它们只不过是对底层容器的接口进行封装而已&#xff0c;所以模拟实现起来比较简单&#xff01;一起来看看是…

vue2初始化项目环境后运行报错: code: ‘MODULE_NOT_FOUND‘, requireStack: []

尝试1&#xff1a; 方式&#xff1a;npm install 重新安装依赖 结果&#xff1a;无效 尝试2&#xff1a; 方式&#xff1a;参照这篇博客对node_modules文件下.bin中的vue-cli-service.cmd内容进行修改 结果&#xff1a;有效 尝试3&#xff1a; 方式&#xff1a;突然意识…

pxe远程安装

PXE 规模化&#xff1a;可以同时装配多台服务器 自动化&#xff1a;自动安装操作系统和各种配置 不需要光盘U盘 前置需要一台PXE服务器 pxe是预启动执行环境&#xff0c;再操作系统之前运行 实验&#xff1a; 首先先关闭防火墙等操作 [rootlocalhost ~]# systemc…

【busybox记录】【shell指令】uniq

目录 内容来源&#xff1a; 【GUN】【uniq】指令介绍 【busybox】【uniq】指令介绍 【linux】【uniq】指令介绍 使用示例&#xff1a; 去除重复行 - 默认输出 去除重复行 - 跳过第n段&#xff08;空格隔开&#xff09;&#xff0c;比较n1以后的内容&#xff0c;去重 去…

使用Express+Node.js搭建网站

Express是一个基于Node.js平台的快速、开放、极简的Web开发框架。它的作用是专门用来创建Web服务器&#xff0c;与Node.js内置的http模块功能相似&#xff0c;但更为简便和高效。 Express中文官网&#xff1a;Express - 基于 Node.js 平台的 web 应用开发框架 - Express中文文…

赶紧收藏!2024 年最常见 100道 Java 基础面试题(三十三)

上一篇地址&#xff1a;赶紧收藏&#xff01;2024 年最常见 100道 Java 基础面试题&#xff08;三十二&#xff09;-CSDN博客 六十五、说一下JSP的4种作用域&#xff1f; 在JSP&#xff08;JavaServer Pages&#xff09;中&#xff0c;作用域&#xff08;Scope&#xff09;是…

Vulnhub项目:NAPPING: 1.0.1

1、靶机介绍 靶机地址&#xff1a;Napping: 1.0.1 ~ VulnHub 2、渗透过程 老规矩&#xff0c;先探测&#xff0c;靶机ip&#xff1a;192.168.56.152 本机ip&#xff1a;192.168.56.146 来看一看靶机开放哪些端口&#xff0c;nmap一下 nmap -sS -sV -A -T5 192.168.56.152 开…

k8s ReplicaSet

ReplicaSet 是替代 ReplicationController 的&#xff0c;ReplicaSet 的行为与 ReplicationController 完全相同&#xff0c; 但pod 选择器的表达能力更强。 ReplicaSet 和 ReplicationController 的区别&#xff1a; ReplicationController 的标签选择器只允许包含某个标签的…

jsx基本语法

JSX语法规则 1、定义虚拟DOM&#xff0c;不要写引号&#xff0c;因为不是字符串&#xff1b; 2、标签中混入js表达式要使用 { } const myId aTgUiGu const myData HeLlo,rEact //1.创建虚拟DOM const VDOM ( <h2 id{myId.toLowerCase()}><span>{myData.toLowerCa…

redisson 使用脚本实现判断元素不在队列中则插入的原子操作

脚本逻辑&#xff1a; 取出队列所有元素遍历元素查找值是否存在不存在则推入 final String scriptText """local valuesInTarget redis.call(lrange, KEYS[1], 0, -1);local index 0;for i, v in ipairs(valuesInTarget) doif v value thenindex ibreake…

基于SpringBoot的大学生心理咨询系统

项目介绍 基于Spring Boot技术栈构建的大学生心理咨询系统&#xff0c;旨在提供一个全方位、定制化的心理健康管理平台。系统采用前后端分离架构&#xff0c;后端利用Spring Boot框架进行深度二次开发&#xff0c;以实现高效稳定的服务端逻辑处理和数据交互&#xff1b;前端界…

Dynamics 365: 从0到1了解如何创建Custom API(3) - Custom API的调试之插件调试

对于Custom API的调试&#xff0c;主要有三种方式&#xff1a; 插件代码中添加log插件调试单元测试 对于这三种方式&#xff0c;说白了也就相当于两种&#xff0c;第一种打log&#xff0c;这种方式很多时候我们是在插件调试突然不好使的时候&#xff0c;或者在不调试时还想看…

7-zip下载、安装

7-Zip 官方中文网站 (sparanoid.com) 7-Zip - 程序下载 (sparanoid.com)

【Linux】文件内容相关的命令,补充:管道符

1、查看文件内容 &#xff08;1-1&#xff09;查看文件内容&#xff1a;cat&#xff0c;tac&#xff0c;head&#xff0c;tail 查看文件内容cat 文件名查看文件内容并显示行号cat -n 文件名倒着查看文件内容&#xff08;从最后一行开始&#xff09;tac 文件名查看文件前10行…

latex参考文献引用网址,不显示网址问题

以引用UCI数据集为例 1、加入宏包 \usepackage{url} 2、在参考文献bib文件中加入网址文献 misc{UCI, author {{D. Dua, E. Karra Taniskidou}}, year {2024}, title {UCI Machine Learning Repository}, howpublished {\url{http://archive.ics.uci.edu/ml}} } 完成&#x…

【机器学习系统的构建】从模型开发的过程讲清楚K-Fold 交叉验证 (Cross-Validation)的原理和应用

0、前言 最近在学习集成学习的时候了解到了k折交叉验证&#xff0c;其实在之前学习吴恩达老师的课程中也学过交叉验证&#xff0c;但是当时也不是很明白。这次借着自己的疑问以及网上搜找资料&#xff0c;终于把交叉验证给弄明白了。 在弄清楚前&#xff0c;我有这样几个疑问…