基于websocket的跨平台通信——iPhone/iPad/Mac控制树莓派(二):Swift控制端搭建

  • 瞎扯
  • 不想看我瞎扯直接跳到这
  • 思路/接口说明
  • 代码实现
    • 创建工程以及导入库
      • 创建工程
      • 导入库
    • 网络延迟计算
    • 数据类定义
      • 发送(暂时用不上,因为暂时没有发送功能)
      • 接收
      • 工厂模型
      • 网络延迟工厂
    • WebSocket
  • 说明

基于websocket的跨平台通信——iPhone/iPad/Mac控制树莓派

瞎扯

为什么你要用苹果平台不搞安卓/Windows呢?
主要是苹果生态比较完善,Swift直接跨所有设备,加上我手上没有安卓设备和Win的PC。(PC装的是Ubuntu)

那你跨平台为什么不写前端或者flutter之类的呢?
不会…

不想看我瞎扯直接跳到这

我们先来实现最基本的,网络延迟检测,也就是检测树莓派等设备的数据发送到控制端的网络延迟。

思路/接口说明

接口说明见:基于websocket的跨平台通信——iPhone/iPad/Mac控制树莓派(一):后端搭建

由于后端只是单纯的做了一个数据转发的功能,控制端和设备端的代码量就要多许多(工厂模型);而众所周知客户端的项目代码结构有很多种(我倾向于使用Redux),所以这里我只列出核心逻辑代码,UI实现等就省略了。

当然有一点还是要说明的:我的代码使用SwiftUI以及SwiftUI生命周期。

代码实现

创建工程以及导入库

创建工程

我打算在各种苹果设备上都能运行,所以这里选择Multiplatform App:

这里我倾向于勾上Core Data,虽然现在没用上,但万一以后要用也就省了步事。

(git选不选无所谓)

导入库

由于众所周知的网络原因,我无法使用Cocoapods等包管理工具导入;所以我选择用Xcode自带的导入工具本地导入。

打开gitclone,搜索starscream,这是Swift的一个websocket库;或者直接复制地址下载到本地:

git clone https://gitclone.com/github.com/daltoniam/Starscream

然后选择Xcode的File->Add Packages…

选择下方的Add Local…

选择克隆下来的Starscream文件夹,点击Add Packages:

然后在Xcode中点开.xcodeproj文件,找到图中的位置,点击左下方的+号:

(稍后iOS下面的macOS也要进行相同的操作)

选择Starscream并点击Add:

就可以测试import starscream看看有没有报错了。

网络延迟计算

树莓派发送一个包含当前时间戳(单位:毫秒)的数据,控制端设备接收后用当前时间戳减去接收到的时间戳,即为网络延迟。

不过为了计算方便以及减少数据量,我们只发送毫秒时间戳的后五位,也就是只考虑百秒内的网络延迟,所以接收到的是一个Int值而不是Int64(Long)。

数据类定义

根据后端的接口定义,可以很快的写出发送和接收的数据类:

发送(暂时用不上,因为暂时没有发送功能)

// BaseMsg.swiftstruct BaseMsg: Codable {var type: Intvar toPlatform: [String]var msgType: Stringvar msg: String
}

接收

// Receive.swiftstruct Receive: Decodable {var fromPlatform: Stringvar msgType: Stringvar msg: String
}

获取当前毫秒级时间戳可以写在Date的extension中,方便调用:

// 随便写哪extension Date {var milliStamp : Int64 {let timeInterval: TimeInterval = self.timeIntervalSince1970let millisecond = CLongLong(round(timeInterval*1000))return millisecond}
}

工厂模型

由于之后我计划在树莓派上连接各种设备,同样由苹果设备控制,所以这里我们需要考虑整个项目的结构。我的思路是将不同的数据模块抽象成一个设备(Device),例如在这个小窗中我要显示树莓派的CPU使用率、CPU温度、内存占用,那么我们就把这三个数据抽象成一个**“主控(MasterControl)”的类;在另外一个小窗中我要显示网络延迟,那么就把网络延迟这个数据抽象成“网络延迟(NetDelay)”**类,等等。

那么我们的思路就明确了,定义一个名为Device的父类,所有的设备都继承这个父类:

// Device.swiftimport Foundationclass Device {var device: String              // 设备名var belonged: PlatformName       // 属于哪个平台,暂时无需理会init(device: DeviceName, belonged: PlatformName) {self.device = device.textself.belonged = belonged}// appState是Redux非常重要的一环,简单来说就是应用的所有数据状态;换成自己框架的数据更新方式即可;appState拥有@State标签,更新时驱动界面更新func DecodeAndUpdate(msg: String, appState: AppState) -> AppState {return appState}  // 解码数据并更新应用的数据状态
}

等等…为什么不用我们Swift大名鼎鼎的protocol协议呢?

因为我想把这些工厂类放在一个字典内。(相对空间复杂度,我对时间复杂度敏感的多)
就是通过接收到的msgType判断这个数据需要被解析成哪个类,msgType与对应的工厂类组成键值对保存在字典中。

对了,为了应对之后设备越来越多的情况,我定义了一个枚举类型,表示设备的名称:

// DeviceName.swift
// 这只是一个小小的例子import Foundationenum DeviceName {case Temperature        // 温度;设备温度、环境温度等case NetDelay           // 网络延迟case MasterControl      // 主控
}extension DeviceName {var text: String {switch self {case .Temperature:return "Temperature"case .NetDelay:return "NetDelay"case .MasterControl:return "MasterControl"}}
}

也是msgType的所有情况。(人为规定)

网络延迟工厂

class NetDelay: Device {override init(device: DeviceName, belonged: PlatformName) {super.init(device: device, belonged: belonged)}override func DecodeAndUpdate(msg: String, appState: AppState) -> AppState {var appState = appStateif let data = msg.data(using: .utf8) {if let temp = try? JSONDecoder().decode(Int.self, from: data) {appState.macTerminal.netDelay.delay = Int(Date().milliStamp % 100000) - temp} else {print("decode error.")}} else {print("error.")}return appState}
}struct NetDelayProperty: Decodable {var delay: Int = 0
}

NetDelayProperty结构体代表存储在应用状态(appState)中的网络延迟,用于在UI界面上显示;

上面的工厂类里面有很多意义不明的变量,但是没有关系,这只是个案例,核心就是计算并更新appState中网络延迟的数据,从而驱动界面更新。

WebSocket

终于到了数据接收了。

import Foundation
import SwiftUI
import Starscreamclass WmSocket: WebSocketDelegate, ObservableObject {// url最后一个路径就是你的设备名var wsurl = "ws://localhost:8880/websocket/WMBP"var request: URLRequestvar socket: WebSocketvar isConnected = falseinit(store: Store) {request = URLRequest(url: URL(string: wsurl)!)request.timeoutInterval = 5socket = WebSocket(request: request)socket.delegate = selfsocket.connect()}func didReceive(event: WebSocketEvent, client: WebSocket) {// print("anal...")switch event {// websocket成功连接时调用case .connected(let headers):isConnected = trueprint("websocket is connected: \(headers)")// websocket断开时调用case .disconnected(let reason, let code):isConnected = falseprint("websocket is disconnected: \(reason) with code: \(code)")// 接收到字符类型数据(包括json)时调用case .text(let string):print(string)// 接收到二进制数据时调用case .binary(let data):print("Received data: \(data.count)")case .ping(_):breakcase .pong(_):breakcase .viabilityChanged(_):breakcase .reconnectSuggested(_):breakcase .cancelled:isConnected = false// 发生错误时调用case .error(let error):isConnected = falseprint(error ?? 0)}}
}

这里只需要关心didReceive()就可以了;很明显当接收到json数据时就会进入case .text(let string)中的代码;所以我们只要在这里适时的调用我们的工厂类的函数,解析数据更新状态以更新ui即可。

说明

以上只是整个流程的思路说明;由于代码量比较大所以就不放这了。需要代码的可以评论或者私信。

基于websocket的跨平台通信——iPhone/iPad/Mac控制树莓派(二):Swift控制端搭建,网络延迟显示相关推荐

  1. 基于横向轨迹误差法(Cross-track Error)P 导航二维控制 实现无人机水平面导航控制

    基于横向轨迹误差法(Cross-track Error)P 导航二维控制 实现无人机水平面导航控制 算法核心思想 算法实现方法 实现无人机水平面导航控制 算法优化方向 算法核心思想 首先我们的目的是控 ...

  2. iphone屏幕镜像如何全屏_苹果系统截屏录屏+标记剪辑功能详解( iPhone/iPad/Mac)

    苹果系统中的截屏和录屏.标记和剪辑功能一如它的其他产品设计,做得非常细致.在我们日常的工作中,不免会遇到这些功能,今天小编就给大家详细讲解下苹果系统截屏录屏.标记剪辑功能,希望对大家有所帮助! 001 ...

  3. 基于websocket的简单通信

    首次接触websocket通信协议,不足之处希望指出一起进步. 简述 websocket是基于tcp协议的一种网络通信协议,分为客户端和服务端,可以实现客户端与服务端的双向通信. 与tcp的不同之处是 ...

  4. 苹果xr截屏怎么截_苹果系统截屏录屏+标记剪辑功能详解( iPhone/iPad/Mac)

    苹果系统中的截屏和录屏.标记和剪辑功能一如它的其他产品设计,做得非常细致.在我们日常的工作中,不免会遇到这些功能,今天小编就给大家详细讲解下苹果系统截屏录屏.标记剪辑功能,希望对大家有所帮助! 001 ...

  5. iPhone/iPad与Windows电脑如何快速面对面互传文件?(建议收藏)

    对于苹果全家桶的用户来说,手机.平板和电脑之间的文件传输,Airdrop一键搞定,而Windows用户好像就没有那么容易了,大多数人可能会选择使用微信或QQ的[文件传输助手],方法没有错,但在数量.容 ...

  6. 提出了一种新的基于一致性算法的直流微电网均流和均压二级控制方案 关键词:一致性算法;直流微电网;下垂控制;分布式二次控制

    关键词:一致性算法;直流微电网;下垂控制;分布式二次控制;电压电流恢复与均分;非线性负载;MATLAB/Simulink;顶刊复现, 主题:提出了一种新的基于一致性算法的直流微电网均流和均压二级控制方 ...

  7. 基于Amarok的跨平台音乐播放器:Clementine mac版

    Clementine是一个基于Amarok的跨平台音乐播放器,同时它也支持 Win 和 Mac 平台,属于全栖型选手.Clementine可以将歌曲目录加入"库"中以进行搜索,可以 ...

  8. 【优秀的iPhone/iPad数据恢复工具】Omni Recover for Mac 2.5

    [简介] 今天和大家分享最新的 Omni Recover for Mac 2.5 版本,这是一款Mac上优秀的iPhone/iPad设备数据恢复工具,支持恢复误删除的短信.照片.视频.文档.通话记录等 ...

  9. 如何使用iToolab FixGo for mac修复iPhone/iPad的系统问题

    iToolab FixGo for mac是一款专业的iOS系统修复工具,能够帮助用户修复200+个iOS / iPadOS系统问题,并防止数据丢失,那么iToolab FixGo for mac如何 ...

  10. iPhone/iPad屏幕投屏镜像到PC或Mac上面教程分享

    AirServer是一款Mac应用程序,可将AirPlay / AirTunes的音频,视频,照片,幻灯片和镜像接收功能添加到Mac电脑.它可以实现将iPhone手机或Mac电脑上的媒体文件以及其他操 ...

最新文章

  1. ICRA2021|嵌入式系统的鲁棒单目视觉惯性深度补全算法
  2. 跟踪算法基准--Tracking the Trackers: An Analysis of the State of the Art in Multiple Object Tracking
  3. 如何在Mac OS X上启动PostgreSQL服务器?
  4. thinkPHP5.1.9 vendor 第三方库的使用
  5. HTML基础_Day03
  6. 炫酷引导页带视频源码
  7. 1.4.2.4. SAVING(Core Data 应用程序实践指南)
  8. 【三维路径规划】基于matlab Nsga-2算法求解无人机三维路径规划【含Matlab源码 1455期】
  9. php五行万年历,PHP制作万年历
  10. android 自动安装 解析包错误,安卓android手机安装包频繁提示解析错误解决方法...
  11. 华为电脑Linux进pe,华为 PE-TLOOM 开启USB调试模式
  12. MFC界面编程基础(08):菜单(一)
  13. el-table表格某列添加icon图标
  14. 高并发访问数据库问题
  15. matlab去除图片水印_初试 Matlab 之去除水印
  16. FFmpeg入门详解之117:视频监控的架构和流程
  17. append和extend的区别
  18. SVN 将代码回滚到之前的版本的方法
  19. 简单分布式锁实现(拍黄片)
  20. /dev/mapper/vg_test-lv_root 占用到达100%的解决方法

热门文章

  1. ubuntu 安装音乐播放器
  2. html校验邮箱格式,正则验证邮箱格式
  3. Learning Conditioned Graph Structures for Interpretable Visual Question Answering论文解读
  4. debian linux上usb摄像头,[Debian] 安装USB摄像头(芯片ZC0301)驱动
  5. 带通滤波器作用和用途_什么是带通滤波器?工作原理及原理图详解
  6. 康托尔集的物理意义1.2
  7. C语言判断一个数是奇数还是偶数
  8. MySQL INSERT对表中数据的操作-插入数据
  9. Openwrt:icmpv6_send: no reply to icmp error
  10. 2012考研数学二第(21)题——中值定理:零点定理+数列极限:单调有界准则