iOS 开源库源码分析之GCDAsyncSocket

iOS 开源库源码分析之GCDAsyncSocket

GCDAsyncSocket

连接

  • 预连接,检查delegate,delegateQueue,是否已经连接,支持IPv4/IPv6,
  • lookupHost,获取server地址
  • lookup,建立连接
  • connectWithAddress4:address6:,通过lookup调用
  • 调用createSocket,创建客户端socket
  • connectSocket,调用connect()函数连接服务器

服务器开始监听(acceptOnPort)

这里最终调用的是acceptOnInterface方法

  • 通过getInterfaceAddress4:address6:获取本地的IPv4/IPv5 地址以及端口
  • 创建IPv4/IPv6的socket,并且进行bind本机地址
  • 调用listen函数进行监听这个socket
  • 获取客户端连接回调,创一个DISPATCH_SOURCE_TYPE_READ的source并且启动,然后设置event hanlder,这里面会调用doAccept方法,在这个方法,通过accept()函数获取客户端新连接的socket,然后会异步到delegateQueue,调用代理方法socket:didAcceptNewSocket:来获取新的连接,

发送数据

发送数据的对象是GCDAsyncWritePacket

发送的方法是writeData:

  • 异步到socketQueue,把GCDAsyncWritePacket对象加入到writeQueue
  • 调用maybeDequeueWrite,取出writeQueue第一个数据为currentWrite
  • 写入数据有三种方式
  • 通过write()函数正常写入
  • TLS方式,通过CFWriteStreamWrite写入
  • SSL方式,通过SSLWrite写入,在这里面如果遇到了I/O阻塞(errSSLWouldBlock),会把数据放入缓冲区进行再次写入

TCP粘包

TCP是面向连接的传输层协议,TCP连接只能是一对一的,它提供可靠的交付服务,也就是说,通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达,TCP提供全双工通信,TCP是面向字节流的,无消息保护边界,TCP把应用程序交下来的数据块看成无结构的字节流,TCP不保证接收方应用程序收到的数据块和发送方应用程序所发出的数据块具有对应的大小关系(例如,发送方应用程序交给发送方TCP共10个数据块,但接收方的TCP可能只用4个数据块就把收到的字节流交付给了上层的应用程序,但接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样)。

基于上面TCP所以会有粘包的问题,而UDP基于数据包则不会出现粘包。但是由于UDP传输不可靠,会出现丢包,无序的问题,而TCP则不会有。

解决思路:

  • 包尾加上\r\n分隔符,作为消息保护边界,缺点是内容中有\r\n会有误判
  • 使用包头,包头内加上包体长度

包头可以是一个字典,里面加上包体的长度的key/value即可。

在包头后面加上[GCDAsyncSocket CRLFData]就是\r\n了,作为分隔符,HTTP就是使用\r\n作为包头分隔符的。

在服务端可以在接受连接的时候

[newSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:110];

来读取一个包头。

获取到包头后,拿到大小,通过下面的方法就可以拿到包体了。

[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1

tag:110];

作为ios开发者学习环境很重要,这是小编 iOS学习交流群 786735421,水群勿扰 !!!水群勿扰!!!内有iOS企业案例学习资料 面试资料和 零基础入门教程 ,欢迎各位小伙伴入群学习交流。

心跳

心跳机制是判断客户端与服务端双方是否存活。

好处是:断了之后客户端可以重新建连,而对于服务端来说清理无效连接。

心跳客户端和服务器都可以发起,一般来说客户端发,一般实现步骤如下

  • 客户端每隔一个时间发送一个包给服务器,并且设置超时时间
  • 服务器收到后回应一个包
  • 客户端如果收到包则说明正常,超时的话则说明挂了

客户端负载均衡

负载均衡(Load Balance)一般指服务端负载均衡,意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。对于服务端负载均衡一般都会维护一个服务器清单,当客户端请求到来时负载均衡服务器会从清单中选出一台处理请求。

而客户端负载均衡最大的区别在于服务器清单是在客户端维护。

实现步骤:

  • 获取服务器列表并且缓存在本地
  • 对每个服务器进行连接并且发送测速包
  • 当全部拿到测速结果(一次来回的时间等)后,找到最优的服务器进行连接

TLS

https://github.com/qinlaoban/Scoket_ssl

关于ssl的握手过程可以参考我之前写的SSL。

可以看一下下面方法,表示开启TLS

- (void)startTLS:(NSDictionary *)tlsSettings

里面有很多key,比如

  • GCDAsyncSocketManuallyEvaluateTrust,如果是YES,表示需要手动验证证书,在socket:didReceiveTrust:completionHandler: delegate方法里面去实现,可以参考 stackoverflow 的实现
  • GCDAsyncSocketUseCFStreamForTLS,仅限iOS,使用CFStream的TLS

startTLS中首先根据tlsSettings构造GCDAsyncSpecialPacket,把packet加入读写队列,这里最终调用的是maybeStartTLS方法,如果不是使用SecureTransport,则调用cf_startTLS方法,如果使用SecureTransport,则调用ssl_startTLS方法。

在ssl_startTLS中

  • 初始化SSL上下文
  • 设置SSL读写的回调
  • 建立SSL连接
  • 当然还有一些其他可选项,比如如果是GCDAsyncSocketManuallyEvaluateTrust的话,会调用SSLSetSessionOption()函数
  • 之后调用ssl_continueSSLHandshake开始握手,调用SSLHandshake()函数进行握手


分享到:


相關文章: