close
当前位置: 物联网在线 > 技术文库 > ios >

iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Read篇)

iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Read篇)

前言:

本篇 ,将重点涉及该框架是如何利用缓冲区对数据进行读取、以及各种情况下的数据包处理,其中还包括普通的、和基于 TLS 的不同读取操作等等。

注:由于该框架源码篇幅过大,且有大部分相对抽象的数据操作逻辑,尽管楼主竭力想要简单的去陈述相关内容,但是阅读起来仍会有一定的难度。如果不是诚心想学习 IM 相关知识,在这里就可以离场了...

注:文中涉及代码比较多,建议大家结合源码一起阅读比较容易能加深理解。

或者自行查阅。

目录:

1.浅析 Read 读取,并阐述数据从 socket 到用户手中的流程。

2.讲讲两种 TLS 建立连接的过程。

3.深入讲解 Read 的核心方法--- doReadData 的实现。

正文: 一.浅析 Read 读取,并阐述数据从 socket 到用户手中的流程

大家用过这个框架就知道,我们每次读取数据之前都需要主动调用这么一个 Read 方法:

[gcdSocket readDataWithTimeout:-1 tag:110];

设置一个超时和 tag 值,这样我们就可以在这个超时的时间里,去读取到达当前 socket 的数据了。

那么本篇 Read 就从这个方法开始说起,我们点进框架里,来到这个方法:

- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag { [self readDataWithTimeout:timeout buffer:nil bufferOffset:0 maxLength:0 tag:tag]; } - (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset tag:(long)tag { [self readDataWithTimeout:timeout buffer:buffer bufferOffset:offset maxLength:0 tag:tag]; } //用偏移量 maxLength 读取数据 - (void)readDataWithTimeout:(NSTimeInterval)timeout buffer:(NSMutableData *)buffer bufferOffset:(NSUInteger)offset maxLength:(NSUInteger)length tag:(long)tag { if (offset > [buffer length]) { LogWarn(@"Cannot read: offset > [buffer length]"); return; } GCDAsyncReadPacket *packet = [[GCDAsyncReadPacket alloc] initWithData:buffer startOffset:offset maxLength:length timeout:timeout readLength:0 terminator:nil tag:tag]; dispatch_async(socketQueue, ^{ @autoreleasepool { LogTrace(); if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites)) { //往读的队列添加任务,任务是包的形式 [readQueue addObject:packet]; [self maybeDequeueRead]; } }}); }

这个方法很简单。最终调用,去创建了一个 GCDAsyncReadPacket 类型的对象 packet ,简单来说这个对象是用来标识读取任务的。然后把这个 packet 对象添加到读取队列中。然后去调用:

[self maybeDequeueRead];

去从队列中取出读取任务包,做读取操作。

还记得我们之前 Connect 篇讲到的 GCDAsyncSocket 这个类的一些属性,其中有这么一个:

//当前这次读取数据任务包 GCDAsyncReadPacket *currentRead;

这个属性标识了我们当前这次读取的任务,当读取到 packet 任务时,其实这个属性就被赋值成 packet ,做数据读取。

接着来看看 GCDAsyncReadPacket 这个类,同样我们先看看属性:

@interface GCDAsyncReadPacket : NSObject { @public //当前包的数据 ,(容器,有可能为空) NSMutableData *buffer; //开始偏移 (数据在容器中开始写的偏移) NSUInteger startOffset; //已读字节数 (已经写了个字节数) NSUInteger bytesDone; //想要读取数据的最大长度 (有可能没有) NSUInteger maxLength; //超时时长 NSTimeInterval timeout; //当前需要读取总长度 (这一次read读取的长度,不一定有,如果没有则可用maxLength) NSUInteger readLength; //包的边界标识数据 (可能没有) NSData *term; //判断buffer的拥有者是不是这个类,还是用户。 //跟初始化传不传一个buffer进来有关,如果传了,则拥有者为用户 NO, 否则为YES BOOL bufferOwner; //原始传过来的data长度 NSUInteger originalBufferLength; //数据包的tag long tag; }

这个类的内容还是比较多的,但是其实理解起来也很简单, 它主要是来装当前任务的一些标识和数据,使我们能够正确的完成我们预期的读取任务。

这些属性,大家同样过一个眼熟即可,后面大家就能理解它们了。

这个类还有一堆方法,包括初始化的、和一些数据的操作方法,其具体作用如下注释:

//初始化 - (id)initWithData:(NSMutableData *)d startOffset:(NSUInteger)s maxLength:(NSUInteger)m timeout:(NSTimeInterval)t readLength:(NSUInteger)l terminator:(NSData *)e tag:(long)i; //确保容器大小给多余的长度 - (void)ensureCapacityForAdditionalDataOfLength:(NSUInteger)bytesToRead; ////预期中读的大小,决定是否走preBuffer - (NSUInteger)optimalReadLengthWithDefault:(NSUInteger)defaultValue shouldPreBuffer:(BOOL *)shouldPreBufferPtr; //读取指定长度的数据 - (NSUInteger)readLengthForNonTermWithHint:(NSUInteger)bytesAvailable; //上两个方法的综合 - (NSUInteger)readLengthForTermWithHint:(NSUInteger)bytesAvailable shouldPreBuffer:(BOOL *)shouldPreBufferPtr; //根据一个终结符去读数据,直到读到终结的位置或者最大数据的位置,返回值为该包的确定长度 - (NSUInteger)readLengthForTermWithPreBuffer:(GCDAsyncSocketPreBuffer *)preBuffer found:(BOOL *)foundPtr; ////查找终结符,在prebuffer之后,返回值为该包的确定长度 - (NSInteger)searchForTermAfterPreBuffering:(ssize_t)numBytes;

这里暂时仍然不准备去讲这些方法,等我们用到了在去讲它。

我们通过上述的属性和这些方法,能够把数据正确的读取到 packet 的属性 buffer 中,再用代理回传给用户。


(责任编辑:ioter)

用户喜欢...

iOS 动画进阶 - 实现炫酷的上拉刷新动效

最近撸了一个上拉刷新的小轮子,只要遵循一个协议就能自定义自己动效的上拉刷新和加载,我自己也写了几个动效进去,下面是一个比较好的动效的实现过程 先上效果图和 github地址 ,有其...


iOS动画进阶 - 手摸手教你写ShineButton动画

前段时间在github上看见一个非常nice的动画效果,可惜是安卓的,想着用swift写一个iOS版的,下下来源代码研究了一下,下面是我写代码的心路历程 先上图 分析动画过程 刚开始看的时候感觉这...


iOS开发进阶 - 富文本正则替换表情

最近写项目需要用到富文本解析字符串显示表情,下面是我使用正则替换实现富文本的方式,希望能帮助到大家 先上效果图 实现过程中需要用到的知识点 NSRegularExpression(正则表达式) NSM...


iOS IM即时通信之聊天界面UI框架

聊天效果.gif 随便扯扯 公司项目以前就集成环信, 后来不知道什么原因给撤了, 最近又不知道打什么鸡血要上IM, 界面一个礼拜搭建完成, 前前后后两个月一直在改pm, 改接口, 一把心酸一把泪,...


iOS即时通讯,从入门到“放弃”?

前言 本文会用实例的方式,将iOS各种IM的方案都简单的实现一遍。并且提供一些选型、实现细节以及优化的建议。 可以打开项目先预览效果,对照着进行阅读。 言归正传,首先我们来总结一...


iOS 使用 socket 即时通信(非第三方库)

其实写这个socket一开始我是拒绝的。 因为大家学C 语言和linux基础时肯定都有接触,客户端和服务端的通信也都了解过,加上现在很多开放的第三方库都不需要我们来操作底层的通信。 但是来...