iOS 上的 WebSocket 框架 Starscream

iOS 上的 WebSocket 框架 Starscream


传统的网络技术 (也就是 Berkeley sockets) 被认为是可靠和稳定的。但是 Berkeley socket 在某些 web 技术,比如代理和防火墙下不太好使。WebSocket 出现于 2011 年,是一种在客户端和服务端之间建立双向通讯的新技术。WebSocket 比起多个 HTTP 请求来说更有效率并允许长连接。

在 iOS 上使用 WebSocket 并不是那么容易。iOS 和 Mac 库 Starscream 的出现,极大地简化了 WebSocket 的创建和使用。

注:本文假设你熟悉 CocoaPods。如果你不熟悉,请参考我们的 CocoaPods 教程

在本文中,你将在一个叫做 Emoji Commuicator 的 App 中完成网络编程部分。Emoji Communicator 允许你将当前心情用一个 emoji 字符发布给所有连接到该服务的人。

Emoji Communicator 原来的开发者原本打算用 HTTP 请求获取新消息,但这个功能使用 WebSocket 显然更合适。你将使用 Starscream 来连接后台的 Web 服务器。

开始

首先需要一个 Web 服务器。在本文中,你会在本机上启动一个 web 服务器。这个示例的 web 服务器运行在 Node.js 下,使用一小个 Javascript 文件来支持它。

当然,首先需要安装 Node.js。如果不确定是否已经安装,可以在终端窗口中输入命令:

node--version

1

如果报错,请按照下列步骤下载和安装 Node.js。否则,你会看到 Node.js 的版本号,你就可以跳过下一节 Node.js 的下载。

安装和下载 Node.js

在 https://nodejs.org/ 下载 Node.js 的最新安装包 (当前 2016.9.22 的最新版本是)。下载完安装包 (例如 node-v6.6.0.pkg),双击进行安装。参考提示进行,选择默认选项就行了。

安装完后,在终端中检查 Node.js 是否工作正常:

node--version

1

如果你没有看到 6.6.0 (或者你所安装的版本),或者报错,再次检查安装是否正确。

聊天服务器

你将用到一个聊天服务器。从这里下载示例 iOS app 和 Web 服务器代码。解压缩 zip 包到桌面或者某个文件夹。在终端中,切换到该目录,并进入 nodeapp 子目录。

这个程序需要一个第三方模块,需要用 Node.js 的包管理器 npm 来安装。在这个目录中,执行:

npminstallwebsocket

1

输入下列命令启动聊天服务器:

node chat-server.js

1

你会看到类似如下输出:

Tue Sep13201618:54:44GMT-0500(CDT) Serverislisteningonport1337

1

然后在浏览器 Safari 或 Chrome 中打开 frontend.html。 

输入一个昵称,发送一条测试消息。如果要在第二个客户端进行测试,重新打开一个浏览器或标签页,用同一个 Url。用另一个昵称登录,发送一条消息;你会看到消息立即出现在另一个浏览器里。

这充分说明了 WebSocket 的强大之处。每个浏览器都和 web 服务器有一个单独的长连接——没有刷新。当消息到达,服务器会自动向所有连接着的客户端进行广播。返回终端,你可以看到所有的聊天活动:

$nodechat-server.jsTueSep13 2016 18:54:44GMT-0500(CDT)Serverislisteningonport1337TueSep13 2016 18:55:19GMT-0500(CDT)Connectionfromoriginnull.TueSep13 2016 18:55:19GMT-0500(CDT)Connectionaccepted.TueSep13 2016 18:55:34GMT-0500(CDT)Userisknownas:Aaronwithgreencolor.TueSep13 2016 18:55:37GMT-0500(CDT)ReceivedMessagefromAaron:HelloTueSep13 2016 18:58:49GMT-0500(CDT)Connectionfromoriginnull.TueSep13 2016 18:58:49GMT-0500(CDT)Connectionaccepted.TueSep13 2016 18:58:51GMT-0500(CDT)Userisknownas:Jameswithredcolor.TueSep13 2016 18:58:55GMT-0500(CDT)ReceivedMessagefromJames:Thisisprettyslick!TueSep13 2016 18:59:03GMT-0500(CDT)ReceivedMessagefromJames: :]TueSep13 2016 18:59:27GMT-0500(CDT)Peerundefineddisconnected.

WebSockets

HTTP 第一次出现是 1991 年,它设计为一种请求/响应式的通讯机制。Web 浏览器用这种机制工作良好,用户请求 web 页,服务器返回内容。但某些时候,需要有新数据时不经过用户请求就通知用户——也就是,服务器推。

HTTP 协议无法很好地解决推模型。在 websocket 出现前,web 服务通过一系列浏览器刷新机制来实现推模型,但效率无法让人满意。

webSocket 实现了服务端推机制。新的 web 浏览器全都支持 WebSocket,这使得它的使用超级简单。通过 WebSocket 能够打开持久连接,大部分网络都能轻松处理 WebSocket 连接。

WebSocket 通常应用在某些数据经常性或频繁改变的场景。例如 Facebook 中的 web 通知、Slack 中的实时聊天、交易系统中的变化的股票价格。

在 iOS 中使用 WebSocket 比较麻烦,你必须进行大量的设置,而且内置的 API 根本帮不上忙。这时 Starscream 出现了——这个小巧、易于使用的库让你所有的烦恼不翼而飞。

Emoji Communicator

打开 EmojiTransmitter.xcodeproj。在模拟器中运行程序,程序很简单,它需要用户输入一个昵称,然后显示一个界面,让用户选择一个 emoji 发送,并显示任何接收到的 emoji。

这个 App 还没有完成网络部分。你将使用 Starscream 来执行所有的 WebSocket 网络请求。

有许多方法可以将 Starscream 集成到你的项目。CocoaPods 和 Carthage 是两种最常见的包管理器。你两种都可以用,但本文将使用 CocoaPods。

首先,关闭打开的项目。开启终端窗口,将目录切换至项目文件夹。在这个项目中已经有一个配置了 Starscream pod 的 Podfile 文件了。你可以直接安装 pod:

pod repoupdate;pod install

1

当 CocoaPods 结束安装,在 Xcode 8 中打开 EmojiTransmitter.xcworkspace 文件。运行程序,检查 App 是否能够运行。 

打开 ViewController.swift,在 import UIKit 后加入:

importStarscream

1

然后,在 ViewController 类的 username 属性后增加一个属性:

varsocket= WebSocket(url:URL(string:"ws://localhost:1337/")!, protocols: ["chat"])

1

这是创建 WebSocket 连接的核心。注意 URL 的歌声,协议是 ws 而不是 Http/https。protocols 参数指定为 chat,这取决于服务端的实现,这个协议可以被使用,也可能被忽略。在本 demo 中,忽略它即可。 

接着在 viewDidLoad 方法后加入:

deinit {socket.disconnect(forceTimeout:0)socket.delegate = nil}

1

2

3

4

当 View Controller 被销毁时,强制关闭 WebSocket 连接。

在 Starscream 中所有的工作都放在 delegate 中进行。Starscream 也支持闭包,如果你不愿意使用委托的话。

在 ViewController.swift,在 fileprivate 扩展后增加一个扩展:

// MARK: - WebSocketDelegateextension ViewController : WebSocketDelegate {publicfuncwebsocketDidConnect(_ socket: Starscream.WebSocket) {  }publicfuncwebsocketDidDisconnect(_ socket: Starscream.WebSocket, error: NSError?) {  }publicfuncwebsocketDidReceiveMessage(_ socket: Starscream.WebSocket, text: String) {  }publicfuncwebsocketDidReceiveData(_ socket: Starscream.WebSocket, data: Data) {  }}


这 4 个委托方法必须实现,否则代码无法通过编译。

然后,在 viewDidLoad 方法的 super.viewDidLoad() 后面添加:

socket.delegate = selfsocket.connect()


运行程序,输入昵称,点击 Next。返回 Node.js 控制台你将看到有一个连接通知。

现在,你已经能够连接到 Node.js App 了,接下来是发送消息到服务器。

首先,将 sendMessage(_:) 方法修改为:

func sendMessage(_message:String) {  socket.write(string:message)}


这会发送消息(本例中,就是 emoji)到 Node.js 服务器。

然后,在 websocketDidConnect(_:) 方法中加入:

socket.write(string: username)

1

这会在连接建立后发送你在第一个界面中输入的昵称。这个服务器会将第一个接收到的消息当做用户名称。 

在 websocketDidDisconnect(_:error:) 中加入:

performSegue(withIdentifier:"websocketDisconnected", sender: self)

1

无论什么原因,只要 socket 被断开,这都会让用户返回到输入昵称界面。如果在你自己的 App 中,你应当在这里进行更健全的错误处理。

接着,在 websocketDidReceiveMessage(_:text:) 方法中:

// 1guardletdata = text.data(using: .utf16),letjsonData =try? JSONSerialization.jsonObject(with: data),letjsonDict = jsonDataas? [String: Any],letmessageType = jsonDict["type"]as? Stringelse{return}// 2ifmessageType =="message",letmessageData = jsonDict["data"]as? [String: Any],letmessageAuthor = messageData["author"]as? String,letmessageText = messageData["text"]as? String {  messageReceived(messageText, senderName: messageAuthor)}


收到的文字消息是可读的字符串——如果是 JSON,尝试将其转为集合对象。代码解释如下:

首先将字符串转为 NSData,然后将 NSData 传给 JSONSerialization 对象以将其转为载体并返回一个有效的对象。最后还检查了几个重要的 key,并设置对应的值。如果对象无效,直接通过 guard 语句退出。

过滤消息的 messageType,然后将数据传递给 messageReceived(messageText:, senderName:) 方法。

下面是一个从 Node.js 收到的 JSON 格式的消息示例:

{  "type":"message",  "data":{    "time":1472513071731,    "text":":]",    "author":"iPhone Simulator",    "color":"orange"}}


运行 app,每当你发送一条消息,emoji 将会用你选择的 emoji 和昵称刷新。返回 web 控制台,你的 emoji 消息也会显示。

这就是 Starscream 的使用!

结束

这里下载最终完成的项目。

Emoji Communicator 是一个使用 WebSocket 的最简单的例子。如果你想在已经存在的服务中使用 Starscream,你可以参考更多资料:

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

参考 Mozilla 开发者网络 上关于 WebSocket 的介绍和如何实现 WebSocket。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容