WebSocket 及 SocketRocket 库

参考资料:RFC6455 - The WebSocket Protocol

1. WebSocket

1.1 概述

WebSocket 协议是独立的,基于 TCP 的协议,它使用单个 TCP 连接进行双向传输。WebSocket 协议被设计来取代现有的使用 HTTP 的双向通信技术。

过去,创建在客户端和服务器之间双向通信,需要一个 HTTP 连接来轮询服务器进行更新
坏处:
1.服务器被迫为每个客户端使用一些不同的底层TCP连接: 一个用于发送信息到客户端和一个新的用于每个传入消息。
2.线路层协议有较高的开销,因为每个客户端-服务器消息都有一个HTTP头信息。
3.客户端脚本被迫维护一个传出的连接到传入的连接的映射来跟踪回复。

WebSocket 协议有两部分:握手和数据传输。

当客户端和服务器握手成功,便可以开始数据传输。 WebSocket 连接建立一个每一端都可以使用的双向通信信道,彼此独立,不必等待请求,随意发送数据。且只要建立起 WebSocket 连接,就能一直保持连接状态直到收到关闭请求,减少通信量。

WebSocket 连接的线路上,一个消息由一个或多个帧组成。 WebSocket 的消息并不一定对应于一个特定的网络层帧,可以作为一个可以被一个中间件合并或分解的片段消息。

一个数据帧可以被客户端或者服务器在打开阶段握手完成之后和端点发送 Close 帧之前的任何时候传输。

帧有相应的类型。 属于相同消息的每一帧包含相同类型的数据。 从广义上讲,有文本数据类型、二进制数据类型、和控制帧类型。

1.1 握手

1.1.1 打开阶段握手

WebSocket 的握手兼容基于 HTTP 的服务器软件和中间件,以便单个端口可以用于 HTTP 客户端和 WebSocket 客户端。
WebSocket 客户端的握手是一个 HTTP Upgrade 请求:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

如果服务器接受该连接,为了证明收到的握手,服务器将源自客户端握手中的 Sec-WebSocket-Key 头信息与规定的字符串连接,使用 SHA-1 散列及 base64 编码后,在 Sec-WebSocket-Accept 头字段中回应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

如果 Sec-WebSocket-Accept 不能匹配、或头字段缺失、或 HTTP 状态码不是101,则连接将不能建立,WebSocket 帧将不发送。

1.1.2 关闭阶段握手

两个节点中的任一个都能发送一个控制帧与包含一个指定控制序列的数据来开始关闭阶段握手。 在收到这样一个控制帧后,另一个节点如果还没发送过 Close 帧,则会返回一个 Close 帧作为响应。在收到这个 Close 帧后,第一个节点关闭连接。

发送一个控制帧之后,表示连接将被关闭,一个节点不会发送任何更多的数据;在接收到一个控制帧之后,表示连接将被关闭,一个节点会丢弃收到的数据。

关闭阶段握手目的是完成 TCP 关闭握手(FIN/ACK),基于 TCP 关闭阶段握手不总是可靠,通过发送一个 Close 帧并等待响应的 Close 帧,某些情况下可避免数据不必要的丢失。

2. 数据帧

WebSocket 协议中,数据通过帧序列来传输。为避免混淆网络中间件(例如拦截代理)和出于安全原因,**客户端必须掩码(mask)**它发送到服务器的所有帧。(不管 WebSocket 协议是否运行在TLS上,都要进行掩码。)

当收到一个没有掩码的帧时,服务器必须关闭连接。在这种情况下,服务器可能发送一个状态码1002(协议错误)的 Close 帧。 服务器必须不掩码发送到客户端的所有帧。 如果客户端检测到掩码的帧,它必须关闭连接。 在这种情况下,它可能使用状态码1002(协议错误)。

2.1 基本帧协议

基本帧协议定义了带有操作码(opcode)的帧类型、负载长度、和用于“扩展数据”与“应用数据”及它们一起定义的“负载数据”的指定位置。

 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 位 用于描述消息是否结束,如果为 1 则该消息为消息尾部,如果为零则还有后续数据包;
RSV: 3 位 用于扩展定义的,如果没有扩展约定的情况则必须为0;
opcode:4 位 用于描述消息类型,消息类型暂定有15种,其中有几种是预留设置;

第二个字节 8位
mask:最高位用 0 或 1 来描述是否有掩码处理;
payload len:7 位 用于描述消息长度,由于 7 位最多只能描述 127,所以这个值会代表三种情况,一种是消息内容少于 126 存储消息长度,如果消息长度少于 UINT16 的情况此值为 126 ,当消息长度大于 UINT16 的情况下此值为 127。

2.2 分片

分片的主要目的:

  1. 当发送一个未知大小的消息时,可以直接开始而不用缓存那条消息。如果消息不能被分片,那么端点将不得不缓冲整个消息以便在首字节发送之前统计出它的长度。服务器或中间件可以选择一个合适大小的缓冲,当缓冲满时,写一个片段到网络。
  2. 用于多路复用,一个逻辑通道上被一个大消息独占输出通道是不可取的,因此多路复用需要可以分割消息为更小的分段来更好的共享输出通道。

2. SocketRocket

SRWebSocket 是 iOS 中用于建立 WebSocket 连接的一个框架,它是由 Facebook 开源的。

2.1 API 调用

使用 SRWebSocket 建立 WebSocket 连接仅需两步

  1. 实现 SRWebSocketDelegate 代理方法:
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;

@optional
- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;

// Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES.
- (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket;
  1. 创建 SRWebSocket 对象并建立连接
- (void)connectServer:(NSString *)server port:(NSString *)port
{
   
    
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://%@:%@",server,port]]];
    _socket = [[SRWebSocket alloc] initWithURLRequest:request];
    _socket.delegate = self;
    [_socket open];
}

2.2 源码分析

2.2.1 初始化

SRWebSocket 根据是否传入协议和证书信任策略提供了几种的初始化方法,最终都会调用到 _SR_commonInit 方法:

- (void)_SR_commonInit;
{
   
    // 获取schem
    NSString *scheme = _url.scheme.lowercaseString;
    assert([scheme isEqualToString
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值