WebSocketWebSocket

WebSocketWebSocketWebSocketiOSWebSocketStarscreamSwiftGolang

一、什么是 WebSocket ?

TCP

主要过程:

HTTPTCPfull-duplexpingpang

最终,使得 “服务端” 拥有 “主动” 发消息给 “客户端” 的能力。

这里有几个重点:

WebSocketTCPHTTPTCP

二、WebSocket 应用场景

1. IM(即时通讯)

WebSocketWebSocket

2. 游戏(多人对战)

典型例子:王者荣耀等(应该都玩过)

3. 协同编辑(共享文档)

WebSocket

4. 直播/视频聊天

对音频/视频需要较高的实时性。

5. 股票/基金等金融交易平台

对于股票/基金的交易来说,每一秒的价格可能都会发生变化。

6. IoT(物联网 / 智能家居)

WebSocket

......
等等等等

WebSocket

三、WebSocket 底层原理

WebSocketHTTPTCP/IP
WebSocketHTTPTCP

1、握手阶段

Golang
GET /chat HTTP/1.1
Host: 127.0.0.1:8000
Origin: ws://127.0.0.1:8000
Qi-WebSocket-Version: 0.0.1
Sec-WebSocket-Version: 13
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: aGFjb2hlYW9rd2JtdmV5eA==
swift
"Upgrade": "websocket", 
"Connection": "Upgrade", 
"Sec-WebSocket-Accept": "NO+pj7z0cvnNj//mlwRuAnCYqCE="
Sec-WebSocket-Acceptbase64(hsa1(sec-websocket-key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))
Sec-WebSocket-AcceptSec-WebSocket-Accept dismatchWebsocketonopen

2、传输阶段

WebSocketframeframe

这样做会有几个好处:

HTTPchunk
WebSocket
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

具体的参数说明如下:

  • FIN(1 bit):
    表示信息的最后一帧,flag,也就是标记符。
    PS:当然第一个消息片断也可能是最后的一个消息片断;

  • RSV1、RSV2、RSV3(均为1 bit):
    默认均为0。如果有约定自定义协议则不为0,一般均为0。(协议扩展用)

  • Opcode(4 bit):
    定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:

Mask17 bit7+16 bit7+64 bit
7 bit000000011111010125
(7+16) bit1111110(即126)126
(7+64) bit1111111(即127)127
Payload报文长度 所传输的数据大小区间
7 bit [ 0, 125]
7 +16 bit [ 126 , 65535]
7 + 64 bit [ 65536, 2^16 -1]

说明:
传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。
1)如果这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;
2)如果这个值是126,则随后的2个字节表示的是一个16进制无符号数,用来表示传输数据的长度;
3)如果这个值是127,则随后的是8个字节表示的一个64位无符号数,这个数用来表示传输数据的长度。

0 bit14 bit1
maskMasking-key
应用数据的长度 = 负载数据的长度 - 扩展数据的长度Application data = Payload data - Extension data

四、iOS 中 WebSocket 相关框架

WebSocket(iOS客户端):
Socket(iOS客户端):

五、使用Starscream(swift)完成客户端长链需求

首先附上Starscream:GitHub地址

Starsream
Podfile
pod 'Starscream', '~> 4.0.0'
pod install

第二步:实现WebSocket能力。

import StarscreamWebSocket
private func initWebSocket() {
    // 包装请求头
    var request = URLRequest(url: URL(string: "ws://127.0.0.1:8000/chat")!)
    request.timeoutInterval = 5 // Sets the timeout for the connection
    request.setValue("some message", forHTTPHeaderField: "Qi-WebSocket-Header")
    request.setValue("some message", forHTTPHeaderField: "Qi-WebSocket-Protocol")
    request.setValue("0.0.1", forHTTPHeaderField: "Qi-WebSocket-Version")
    request.setValue("some message", forHTTPHeaderField: "Qi-WebSocket-Protocol-2")
    socketManager = WebSocket(request: request)
    socketManager?.delegate = self
}

同时,我用三个Button的点击事件,分别模拟了connect(连接)、write(通信)、disconnect(断开)。

```swift
// Mark - Actions
// 连接
@objc func connetButtonClicked() {
socketManager?.connect()
}
// 通信
@objc func sendButtonClicked() {
socketManager?.write(string: "some message.")
}
// 断开