??斗地主捕鱼电竞提现秒到 广告位招租 - 15元/月全站展示
??支付宝搜索579087183领大额红包 ??伍彩集团官网直营彩票
??好待遇→招代理 ??伍彩集团官网直营彩票
??络茄网 广告位招租 - 15元/月全站展示
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法

转载   鉴皇师   2018-10-31   浏览量:25


国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——可以通俗的解释为服务器主动发送信息给客户端。

区别于MQTT、XMPP等聊天的应用层协议,它是一个传输通讯协议。它有着自己一套连接握手,以及数据传输的规范。

而本文要讲到的SRWebSocket就是iOS中使用websocket必用的一个框架,它是用Facebook提供的。

关于WebSocket起源与发展,是怎么由:轮询、长轮询、再到websocket的,可以看看冰霜这篇文章:

微信,QQ这类IM app怎么做——谈谈Websocket

大家可以关注小编的群:656315826 可以获取相关视频教程和源码哦
二. SRWebSocket的对外的业务流程:

首先贴一段SRWebSocket的API调用代码:国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
要简单使用起来,总共就4行代码,并且实现你需要的代理即可,整个业务逻辑非常简洁。

但是就这么几个对外的方法,SRWebSocket.m里面用了2000行代码来进行封装,那么它到底做了什么?我们接着往下看:

三. SRWebSocket的初始化以及连接流程:

1首先我们初始化:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
会初始化一些属性:

包括对schem进行断言,只支持ws/wss/http/https四种。

当前socket状态,是正在连接,还是已连接、断开等等。

初始化工作队列,以及流回调线程等等。

初始化读写缓冲区:_readBuffer、_outputBuffer。

  1. 输入输出流的创建及绑定:
    国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
    在这里,我们根据传进来的url,类似ws://localhost:80,进行输入输出流CFStream的创建及绑定。

[图片上传失败...(image-677c80-1534401345579)]

到这里,初始化工作就完成了,接着我们调用了open开始建立连接:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法

open方法定义了一个超时,如果超时了还在SR_CONNECTING,则报错,并且断开连接,清除一些已经初始化好的参数。
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法

开始连接主要是给输入输出流绑定了一个runloop,说到这个runloop,不得不提一下SRWebSocket线程的问题:

一开始初始化我们提过SRWebSocket有一个工作队列:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这个工作队列是串行的,所有和控制有关的操作,除了一开始初始化和open操作外,所有后续的回调操作,数据写入与读取,出错连接断开,清除一些参数等等这些操作,全部是在这个_workQueue中进行的。

而这里的runloop:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
是新创建了一个NSThread的线程,然后起了一个runloop,这个是以单例的形式创建的,所以networkThread作为属性是一直存在的,而且起了一个runloop,这个runloop没有调用过退出的逻辑,所以这个networkThread是个常驻线程,即使socket连接断开,即使SRWebSocket对象销毁,这个常驻线程仍然存在。

可能很多朋友会觉得,那我都不用websocket了,什么都置空了,凭什么还有一个常驻线程,不停的空转,给内存和CPU造成一定开销呢?

楼主的理解是,作者这么做,可能考虑的是既然用户有长连接的需求,肯定断开连接甚至清空websocket对象只是一时的选择,肯定是很快会重新初始化并且重连的,这样这个常驻线程就可以得到复用,省去了重复创建,以及获取runloop等开销。

那么SRWebSocket总共就有一个串行的_workQueue和一个常驻线程networkThread,前者用来控制连接,后者用来注册输入输出流,那么为什么这些操作不在一个常驻线程中去做呢?

我觉得这里就涉及一个线程的任务调度问题了,试想,如果控制逻辑和输入输出流的回调都是在同一个线程,对于输入输出流来说,回调是会非常频繁的,首先写_outputStream是在当前流NSStreamEventHasSpaceAvailable还有空间可写的时候,一直会回调,而读_inputStream则在有数据到达时候,也会不停的回调,试想如果这时候,控制逻辑需要做什么处理,是不是会有很大的延迟?它需要等到排在它前面插入线程中的任务调度完毕,才能轮得到这些控制逻辑的执行。所以在这里,把控制逻辑放在一个串行队列,而数据流的回调放在一个常驻线程,两个线程不会互相污染,各司其职。

接着主流程往下走,我们open了输入输出流后,就调用到了流的代理方法了:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这里如果我们一开始初始化的url是 wss/https,会做SSL认证,认证流程基本和楼主之前讲的CocoaAsyncSocket,这里就不赘述了,认证失败,会断开连接,

最终SSL或者非SSL都会走到这么一个方法:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这个方法有点长,大家都知道,WebSocket建立连接前,都会以http请求作为握手的方式,这个方法就是在构造http的请求头。

我们来看看RFC规范的标准客户端请求头:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: //example.com

Sec-WebSocket-Protocol: chat, superchat

Sec-WebSocket-Version: 13

标准的服务端响应头:

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Protocol: chat

这里需要讲的是这Sec-WebSocket-Key和Sec-WebSocket-Accept这一对值,前者是我们客户端自己生成一个16字节的随机data,然后经过base64转码后的一个随机字符串。

大家可以关注小编的群:656315826 可以获取相关视频教程和源码哦

而后者则是服务端返回回来的,我们需要用一开始的Sec-WebSocket-Key与服务端返回的Sec-WebSocket-Accept进行校验:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
服务端这个Accept会用这么一个字符串拼接加密:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这个字符串是RFC规范定死的,至于为什么是这么一串,楼主也不知所以然。

我们发出这个http请求后,得到服务端的响应头,去按照服务端的方式加密Sec-WebSocket-Key,判断与Sec-WebSocket-Accept是否相同,相同则表明握手成功,否则失败处理。
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
至此都成功的话,一个WebSocket连接建立完毕。

四. 接着来讲讲数据的读和写:

当建立连接成功后,就会循环调用这么一个方法:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
记得楼主之前写过一篇即时通讯下数据粘包、断包处理实例(基于CocoaAsyncSocket),因此抛出一个问题,WebSocket需要处理数据的断包和粘包么?

答案是基本不需要。引用知乎上的一段回答:

RFC规范指出,WebSocket是一个message-based的协议,它可以自动将数据分片,并且自动将分片的数据组装。

也就是说,WebSocket的RFC标准是不会产生粘包、断包问题的。无需应用层开发人员关心缓存以及手工组装message。

然而理想与现实的不一致:RFC规范与实现的不一致,现实当中有几个问题:

每个message可以是一个或多个分片。message不记录长度,分片才记录长度。

message最大的长度可以达到 9,223,372,036,854,775,807 字节,是由于Payload的数据长度有63bit的限制。

很多WebSocket的实现其实并不按照标准的RFC实现完全,很多仅仅实现了50%就拿来用了。这就导致了,在WebSocket实现上的最大长度很难达到这个大小,于是,很多API的实现上是会有限制的,可能会限制你的发送的长度,也可能会把过长的数据直接以流式发送。

而SRWebSocket中实现的方式上彻底解决了数据粘包,断包的可能。

数据是通过CFStream流的方式回调回来的,每次拿到流数据,都是先放在数据缓冲区中,然后去读当前消息帧的头部,得到当前数据包的大小,然后再去创建消费者对象consumer,去读取缓冲区指定数据包大小的内容,读完才会回调给我们上层用户,所以,我们如果用SRWebSocket完全不需要考虑数据断包、粘包的问题,每次到达的数据,都是一条完整的数据。

接着我们大概来看看这个流程:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
上面这个方法就是一个读取头部的方法,之前我写过断包粘包的文章就是用一个\r\n来分割头部和正文,这里是用了\r\n\r\n,每次读到这个标识符为止,就是读取了一个完整的WebSocket的消息帧头部。

这里我们先需要说清楚的是,数据一到达,就在stream的代理中回调中,写到了我们的_readBuffer缓冲区中去了:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
接着我们来看添加消费者这个方法:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
其实就是添加了一个stream_scanner类型的对象,到我们的_consumers数组中去了,以后我们读取数据,都会先取出_consumers中的消费者,要读取多少,就给你从_readBuffer里去读多少数据。
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这个方法就是做这么一件事,根据consumer的要求,循环去_readBuffer中读取数据。

至于读的过程,大家可以自己去看下吧,楼主提供的源码注释里已经写的很清楚了,有点略长,这里就不放代码了,方法如下:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
至此我们讲了握手的头部信息的读取,与判断是否握手成功,然后数据到达是怎么从stream到_readBuffer中去的,并且简单介绍了_pumpScanner会根据消费者对象,去从_readBuffer中读取数据,读取完成并且回调consumer的handler

现在我们来讲讲一个数据从头部开始,到内容的读取过程:

每次我们读取新的一帧数据,都会调用这么个方法:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
会清空上一帧的一些信息,然后开始当前帧的读取,我们来简单看看一个WebSocket消息帧里包含什么:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
就是这么一张图,大家应该经常见,这个图是RFC的标准规范。简单的说明下这些标识着什么:

FIN 1bit 表示信息的最后一帧,flag,也就是标记符

RSV 1-3 1bit each 以后备用的 默认都为 0

Opcode 4bit 帧类型,稍后细说

Mask 1bit 掩码,是否加密数据,默认必须置为1

Payload 7bit 数据的长度 (2^7 -1 最大到127)

Masking-key 1 or 4 bit 掩码 //用来编码数据

Payload data (x + y) bytes 数据 //

Extension data x bytes 扩展数据

Application data y bytes 程序数据

更详细的可以看看:WebSocket数据帧规范

接着我们读取消息,会用到其中的一些字段,包括FIN、 MASK、Payload len等等。

然后来看看这个读取当前消息帧的方法:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
这个方法是先去读取了当前消息帧的前2个字节,大概就是这么一部分:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
然后会去对头部信息进行一些判断,但是最主要的还是去获取payload,也就是真实数据的长度,然后还是调用:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
去读取真实数据的长度,然后会在下面这个方法中判断当前帧的数据是否读取完成:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
如果没读取完成,会继续去读取,否则就调用完成的方法,在完成的方法中会回调暴露给我们的代理:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
并且继续去读下一帧的数据
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
整个数据读取过程就完成了。

接着我们来看看数据的写:
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
国内作战指挥学院毕业的程序员解析:美国国防、银行和支付的加密算法
基本上非常简单,区别于之前CocoaAsyncSocket,读和写都没多少代码,原因是因为CocoaAsyncSocket整篇都用的是CFStream等相对上层的API。

SRWebSocket全篇代码注释地址:SRWebSocket注释。

大家可以关注小编的群:656315826 可以获取相关视频教程和源码哦

转载自://blog.51cto.com/14039500/2311167

招聘 不方便扫码就复制添加关注:程序员招聘谷,微信号:jobs1024


上一篇:

iOS开发初学者入门需要学习哪些知识?
都说互联网的快速发展改变着人们的就业生活方式,于是像程序员这样的技术性岗位就成为大家争相追逐的岗位,作为一项技术性iOS开发岗位,高门槛,高收入总是伴随着这一行业,所以现在越来越多的新人选择从事iOS开发工作,其中不乏一些转行的程序员。
iOS将相册中图片上传至服务器的方法
这篇文章主要为大家详细介绍了iOS将相册中图片上传至服务器的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
iOS如何定义名为任意的变量详解
这篇文章主要给大家介绍了关于iOS如何定义名为任意的变量的相关资料,文中通过示例代码介绍的非常详细,对各位iOS开发者们具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
iOS利用NSMutableAttributedString实现富文本的方法小结
这篇文章主要给大家介绍了关于iOS利用NSMutableAttributedString如何实现富文本的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
iOS中精确计算WebView高度的方法示例
这篇文章主要给大家介绍了关于iOS中如何精确计算WebView高度,以及iOS开发之解决WebView自适应内容高度的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧
iOS禁用右滑返回的两种方法
这篇文章主要为大家详细介绍了iOS禁用右滑返回的两种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
iOS录屏和截屏监听的实现代码
本篇文章主要介绍了iOS录屏和截屏监听的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
IOS上架由于金融许可证问题,大部分金融平台处于无法过审的状态怎么解决
苹果Appstore规定:金融理财类应用只能用对应公司名称的开发者账号上传。也就是说,如果用个人开发者账号提交金融理财类应用后收到了类似邮件,可以考虑用公司开发者账号提交;(鉴于您就是使用的开发者账号,所以可以不做考虑。)如果是在帮客户提交应用,可以让客户将你的开发者账号添加到他们的开发者账号团队成员中,然后再尝试提交;审核期间,将敏感信息(例如App中出现的银行名称等)和功能删除或隐藏;(如果...
swift中利用runtime交换方法的实现示例
这篇文章主要给大家介绍了关于swift中利用runtime交换方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。