SocketRocket - facebook
WebSocket 协议
百度百科
http://www.tuicool.com/articles/7zyMvy6
HTML5一种新的协议,实现了浏览器与服务器全双工通信,一开始的握手需要借助HTTP请求完成;
传统的HTTP请求采用轮询,而HTTP的header是非常长的,这样会占用很多带宽;
WebSocket API中,浏览器只需要做一个握手的动作,然后,浏览器和服务器之间形成了一条快速通道,两者之间就直接可以数据互相传送;
HTTP的keep-alive是把多个http请求合并为一个,而Websocket是一个新协议;
Paste_Image.png
多出的 Upgrade,Connection字段表明是websocket请求;
-
数据帧格式
基本的数据帧为一个opcode、一个payload长度和发送的应用数据
Paste_Image.png
1. 测试用例
2. 技术点
NSOperation
NSOperation 方法说明
(void)start; 启动 operation
isFinished, isExecuting 状态标识
在start函数里处理设计要执行的功能
-
设计 operation
设计一个基础的SRTWebSocketOperation<SRWebSocketDelegate>, 并代理处理SR webSocket的close及fail事件;
SRAutobahnOperation : SRTWebSocketOperation 实际设计执行的子operation, 并处理delegate中消息接收的事件;
设计了三个测试接口:
SRAutobahnTestResultOperation 结果
SRAutobahnTestCaseInfoOperation 信息
SRAutobahnTestCaseCountOperation 数目
SRAutobahnTestUpdateReportsOperation 更新
每个接口创建对应的url测试 -
operation queue
NSOperationQueue
addDependency //依赖任务完成偶才开始执行其他任务
addOperation -
__bridge
__bridge 关键字来实现id类型与void*类型的相互转换;
__bridge_retained 是编译器替我们做了retain工作;
__bridge_transfer 是编译器替我们做了release工作;
NSURL 使用
NSURLComponents
NSURLQueryItem
BLOCK
block copy ??? 使用copy有什么用途??//[textMessageHandler copy]
__block 定义变量,让block内部可改变
手动KVO
willChangeValueForKey / didChangeValueForKey 即时状态不变化也能触动observation?
DSP
DSP中的block使用self不会引起循环引用,使用weakSelf的是为了减少self的生命周期;因为block肯定会执行;
- 使用 dispatch_group_t 同步
diapatch_group_create / dispacth_group_enter / dispatch_group_wait / dispatch_group_leave - 使用 dispatch_semaphore_t 进行同步
- dispatch_data_t
某种程度上跟NSData类型相似;
它的独特属性是它可以基于零碎的内存区域,对组合内存进行连接管理;
dispatch_data_t c = dispatch_data_create_concat(a, b);
它并不把数据copy到一个单独的更大的内存区域,相反的只是简单简单持有a和b对象;
类似的,你可以使用dispatch_data_create_subrange来创建一个不做任何拷贝操作的子区域; - dispatch_benchmark
测试代码执行的平均纳秒数,只在调试和性能分析上起作用;
编译相关
attribute ((vector_size (32))); 占32字节
_Alignof(type-name) 某类型的对齐要求
3. 结构设计
Paste_Image.png4. 模块
Internal 模块
SRPinningSecurityPolicy : SRSecurityPolicy
使用 Security 框架
- evaluateServerTrust
遍历serverTrust对象,检查是否与pinnedCertificates中的某个证书一致,当验证所有pinned证书都正确时,返回YES,否则NO; - updateSecurityOptionsInStream
设置NSStream对象的安全属性策略,支持TLS,是否支持证书链;
SRProxyConnect :NSObject
使用 NSStreamDelegate 代理,使用 CFNetwork 框架;
处理url的代理设置,有代理连接代理,无代理直接访问host;
属性:http与socket代理,SSL支持,输入数据数组
- initWithURL:(NSURL *)url
初始化工作,检查url是否需要SSL;
创建写数据的 dispatch_queue,初始化输入数据数组 - _configureProxy 取得系统代理并设置代理
_fetchPAC 根据url自动发现代理,再调用_runPACScript取得代理设置
_runPACScript 运行PAC的JS脚本取得代理设置
_readProxySettingWithType 读取http或socket代理设置保存 - _openConnection
NSStream Foundation架构中定义
_initializeStreams 根据url跟代理配置连接参数,调用CFStreamCreatePairWithSocketToHost 创建socket的read和write stream;
NSInputStream / NSOutputStream open 打开stream; - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
处理socket的read/write stream的代理事件,包括连接、错误、数据事件的处理;
如果stream打开成功,检查是否有http代理,如果有发送http代理连接请求; - _didConnect
表示根据代理设置已连接成功的处理,返回;
设置input/outputStream为nil,取消代理,回调_completion; - _writeData
这个数据只会是代理连接的请求数据;
将数据写入到outStream中,并做错误与超时处理; - _processInputStream
读取inputStream的数据放入到input队列中;
CFHTTPMessageAppendBytes 将数据放入到代理头中,
调用_proxyProcessHTTPResponseWithData检查代理是否连上了;
SRDelegateController :NSObject
设置DSP或者Operation来处理delegate的block;
IOConsumerPool :NSObject
存储管理poolSize个SRIOConsumer;
- consumerWithScanner
从pool中取出一个SRIOConsumer或者创建一个,并设置参数返回consumer; - returnConsumer
向pool中添加一个consumer;
IOConsumer
管理io接口属性
- resetWithScanner
根据传入参数设置stream_scanner / data_callback函数指针,设置_bytesNeeded/_readToCurrentFrame/_unmaskBytes
SRRunLoopThread :NSThread 自定义线程类
通过自定义main 中 runloop来控制循环不退出,同时记录线程的 runloop
- main 函数:添加一个空的 runloop source,并加入到 current runloop中,保持线程一直阻塞等待事件触发?
Utilities 模块
提供各种C接口工具函数
SRHash
对数据进行sha1或者base64编码
SHA1: Secure Hash Algorithm 安全哈希算法
Base64:网络上最常见的用于传输8Bit字节代码的编码方式之一,返回一个字符串;
SRHTTPConnectMessage
根据request,secureKey,协议版本,cookie及协议,来生成一个CFHTTPMessageRef的Http消息头;
CFHTTPMessageSetHeaderFieldValue:设置http消息头的字段,包括GET/Host/Cookie/Authorization/Connection/Sec-WebSocket-Key/Sec-WebSocket-Version/Sec-WebSocket-Protocol以及request中的key字段;
SRURLUtilities
- SRURLRequiresSSL
取得NSURL中的host,port,schema组织成一个 https://127.0.0.1:90像是的字符串; - SRURLRequiresSSL
检查NSURL的schema中是否为wss还是https(表示SSL协议); - SRStreamNetworkServiceTypeFromURLRequest
把NSURLRequest中的networkServiceType转换为NSStream类型的字符串;
服务类型包括:默认正常、VoIP、Video、Background、Voice及Call Signaling;
SRSIMDHelpers
对数据进行SIMD的处理,使用多数据流能够加快数据处理?
- SIMD
SIMD:Single Instruction Multiple Data 单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集;
以加法指令为例,单指令单数据(SISD)的CPU对加法指令译码后,执行部件先访问内存,取得第一个操作数;之后再一次访问内存,取得第二个操作数;随后才能进行求和运算。而在SIMD型的CPU中,指令译码后几个执行部件同时访问内存,一次性获得所有操作数进行运算。这个特点使SIMD特别适合于多媒体应用等数据密集型运算;
Socket Rocket 模块
Paste_Image.pngSRWebSocket
主要连接接口及数据处理在这里面
SRWebSocketDelegate : handle status and message events
Constructor / open
属性:涉及NSURLRequest、SRSecurityPolicy、SRDelegateController、SRIOConsumer等对象;
- (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates
根据请求URL,协议,是否接受不信任的SSL证书,来创建请求;
SRSecurityPolicy用来处理设置stream的安全策略;
这个函数用于处初始化各个参数; - open
超时处理、SRProxyConnect进行连接;
在block中取得连接的 input/outputStream
如果需要验证SSL,在接收到stream数据后对证书进行验证; - didConnect
生成随机key,生成并发送webSocket的http连接请求消息;
_pumpWriting 将数据写入到 _outputStream 发送出去; - sendString
对数据进行frame组织并发送给webSocket服务端;参考webSocket协议;
sendString --> _sendFrameWithOpcode --> SRMaskBytesSIMD + _writeData
10. 名词解释
PAC:代理自动配置,一个PAC文件包含一个JS形式的函数 “FindProxyForURL(url, host)” ;PAC文件中的URL可能是手工配置的,也可能是通过网页的网咯代理自发现协议(Web Proxy Autodiscovery Protocol)自动配置的;