Socket学习总结系列(二) -- CocoaAsyncSocket
前言
这是这个系列文章的第二篇,要是没有看第一篇的还是建议看看第一篇,以为这个是接着第一篇梳理的先大概的总结一下在上篇的文章中说的些内容:
1、 整理了一下做IM我们有那些途径,以及我们怎样选择最适合自己的
2、在做IM的时候协议你又该怎样选择,以及这些协议之间一些的对比等等
3、接下来梳理了一下Socket的我们该怎样理解,它的心跳,pingpong,重连机制等等
4、利用demo整理出来了原生Socket的简单的连接以及接收/发送消息。
内容
1、 对CocoaAsyncSocke这个三方的理解以及一些自己的看法
2、分析CocoaAsyncSocket的集成,源码的一些解析
3、利用CocoaAsyncSocket实现Socket的连接,接收/发送 消息,以及总结一下这整个过程
CocoaAsyncSocke
这里我们先认识一下CocoaAsyncSocke:
CocoaAsyncSocke是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的、强大的异步套接字库,向上封装出简单易用OC接口。省去了我们面向Socket以及数据流Stream等繁琐复杂的编程,下面是我们导入的整个框架的

GCDAsyncUdpSocket 是基于UDP协议写的
以前框架还有一个runloop版,不过因为功能相似等其他的原因,后续版本就废弃了,现在仅有这个GCD版本。

CocoaAsyncSocket源码 (建议先文章最后下载Demo)

第二部分:这一部分的内容就是代理和线程的设置,说实话也没什么好说的,重点还是下面的连接部分,这个你也在Demo中配合注释去理解理解。
第三部分:这一部分比起前面的两小部分稍微就需要我们注意点了,这部分的内容在下面的重点的连接部分用的比较多,你仔细看这部分方法的名字也可以理解,都是一些设置、判断IPV4和IPV6是否可用的方法。至于最下面userData的复制方法,这点我觉得似乎可以暂时忽略。
但按照我自己的理解,很少用OC来写服务端的代码吧!当然这也许也只是我自己见的少而已吧,我是真的不怎么知道用OC来写服务端,不过这部分的代码能能帮助我们理解在整个过程中服务端的Accept到底是怎么一个流程:
三:Connect
连接这部分的代码可以说是这整个三方的核心内容,先看看我们划分的它的方法架构:

接下来把这五部分我们说说:
第一部分: 前置判断,这一部分的内容是在调用了连接方法之后在连接的方法里面调用的,我们在这里先不说它的调用时具体在连接方法哪里调用,怎么调用的我们先看看这个前置检查到底检查了什么,看看里面的内容,等到我们看到调用它的地方的时候我们再谈。
注意:下面的代码不是完整的,完整版本看Demo,具体判断之后返回YES还是NO看具体的情况而定,我们这里是为了不让无用代码占篇幅,我们的注意点放在它是通过哪些条件作了前置的判断,可以看代码中的注释:
{
// 先断言,如果当前的queue不是初始化quueue,直接报错
// dispatch_get_specific 这个和dispatch_set_specific的用法具体的可以百度
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//无代理
if (delegate == nil) // Must have delegate set{
}
//没有代理queue
if (delegateQueue == NULL) // Must have delegate queue set{
}
//当前不是非连接状态
if (![self isDisconnected]) // Must be disconnected{
}
// 判断是否支持IPV4 IPV6 &按位“与”运算,因为枚举是用 左位移<<运算定义的,所以可以用来判断 config包不包含某个枚举。因为一个值可能包含好几个枚举值,所以这时候不能用==来判断,只能用&来判断
// 注意这个解释:kIPv4DisabledIf set, IPv4 is disabled,要是包含就说明IPV4是不能使用的,也就是要是返回YES,说明IPV4不能使用
BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO;
BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO;
//是否都不支持
if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled{
}
// 如果有interface,本机地址
// interface这个参数这个就是我们设置的本机IP+端口号
/*
一般情况不需要去设置这个参数,默认的为localhost(127.0.0.1)本机地址。而端口号会在本机中取一个空闲可用的端口。
而我们一旦设置了这个参数,就会强制本地IP和端口为我们指定的
这里端口号如果我们写死,万一被其他进程给占用,讲导致无法连接成功
*/
if (interface)
{
NSMutableData * interface4 = nil;
NSMutableData * interface6 = nil;
//得到本机的IPV4 IPV6地址
[self getInterfaceAddress4:&interface4 address6:&interface6 fromDescription:interface port:0];
//如果两者都为nil
if ((interface4 == nil) && (interface6 == nil)){
}
//IPV4不能正常使用且本机的IPV6为nil
if (isIPv4Disabled && (interface6 == nil)){
}
//IPV6不能正常使用且本机的IPV4为nil
if (isIPv6Disabled && (interface4 == nil)){
}
//如果都没问题,则赋值
connectInterface4 = interface4;
connectInterface6 = interface6;
}
// Clear queues (spurious read/write requests post disconnect)
// 读写Queue清除
// 走到这里则前面的全没有返回值,在这里就返回YES,
[readQueue removeAllObjects];
[writeQueue removeAllObjects];
//能走到这里的条件 有delegate delegateQueue 包含IPV4或者IPV6
return YES;
}
注意:还有一个前置检测方法,我们在这里就不粘贴代码了。你看了第一个你也能看的懂第二个的啊判断条件,至于为什么会有两个前置检测的方法,怎么调用这个我们接着看。
第二部分:逐层调用连接方法 你在这三个逐层调用的连接方法里面可以看到下面这段代码,在这 block 中你可以看到在这里调用了我们上面说的第一个前置检测方法:

在这里做了前置判断通过之后,再往下面就是异步执行获取得到IPV4的地址:address4 和IPV6的地址:address6 ,获取的具体方法你可以在Demo中看看,在这里获取到之后就进入我们需要理解的三部曲连接终极方法了,这三个方法先知道有三个,我们在说完下面的地址调用连接方法之后会说这三个方法,就在这个block的最后,发起了调用终极连接三部曲:
//异步去发起连接
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool { [strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
这个block的调用就在这个block的下面。
第三部分: 直接连接一个addr的data 三个逐层连接方法,这一部分的内容在我们日常的使用中使用的也不是很多,具体的在注释中也有,你也可以按照前面我们说的去理解这部分的逻辑。
第四部分: 我们前面说的终极连接三方法都是在这一部分里面的,在这部分我们说说这三个方法,还有我们前面需要补充的问题,就是为什么有两个前置检测方法,哪里用到了呢?
下面这三个方法是终极的连接方法,这三个也是逐层的调用连接,返回值以及里面具体的调用还有方法里面的内容注释里面都写得比较清楚,大家看Demo。
// 下面三个是终极的连接方法
- (void)lookup:(int)aStateIndex didSucceedWithAddress4:(NSData *)address4 address6:(NSData *)address6{} - (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr{} - (void)connectSocket:(int)socketFD address:(NSData *)address stateIndex:(int)aStateIndex{}
再说说我们遗留下来的那个问题,另一个前置方法在哪里用?看下面代码:
//连接本机的url上,IP8C,进程间通信
- (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;
{
LogTrace(); __block BOOL result = NO;
__block NSError *err = nil; dispatch_block_t block = ^{ @autoreleasepool { //判断长度
if ([url.path length] == 0)
{
NSString *msg = @"Invalid unix domain socket url.";
err = [self badParamError:msg]; return_from_block;
} // Run through standard pre-connect checks
//前置的检查
if (![self preConnectWithUrl:url error:&err])
{
return_from_block;
} // We've made it past all the checks.
// It's time to start the connection process. flags |= kSocketStarted; // Start the normal connection process NSError *connectError = nil;
//调用另一个方法去连接,连接Unix域服务器
if (![self connectWithAddressUN:connectInterfaceUN error:&connectError])
{
[self closeWithError:connectError];
return_from_block;
} [self startConnectTimeout:timeout]; result = YES;
}}; //在socketQueue中同步执行
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
else
dispatch_sync(socketQueue, block); if (result == NO)
{
if (errPtr)
*errPtr = err;
} return result;
}
首先这个方法是在didConnect 方法里面去调用的,这个didConnect就是已经连接成功的方法,在这个放里面调用我们上面的方法,然后再上面给的方法里面你就可以看到前置检查方法和连接Unix域服务器的方法,这里我想大家就明白了,在连接Unix域服务器的时候用到了前置检查,这个也是在服务端用到的,大家要是感兴趣可以去看看里面的具体的代码注释。
下面Diagnostics部分的代码结构如下,这里全都是在配合我们上面连接部分的代码所用:

上面的第五部分你看方法名称也就知道,这里我们就不在多说这部分的内容。
接下来我们说说剩下的主要的两部分,读和写这两部分:
三:Writing

这部分是写的内容,通过这几个方法就完成了一个写数据的操作,当然这写方法里面肯定还是会涉及到其他的一些辅助的方法,这里我们不一一的列举了,大家在Demo里面去看,再说一点,这部分的代码你根据demo看注释之前,还是先把上篇我们说的那个Socket原生的发送和接收过程理解了,这样有助于你更好的看完写部分的代码,发送完了之后接下来我们就是要看接收的代码了。我们看接收部分的代码。
四:Reading

上面最重要的就是这个方法: doReadData
上面这个方法后面我们添加的几个标签(开始读取数据 CFStream , 开始读取数据 SSLRead, 开始读取数据普通的形式 等等)都是对这个方法的解释。
当到下面的 completecurrentread 完成当前的读操作,到下面这里的时候:

在这里就调用了我们GCDAsyncSocket中接收消息的代理方法:

这里我们的读的操作你也就理解了,当然我说的不是看看这样一个过程你就理解了,重点还是我们Demo里面CocoaAsyncSocket的注释!!
剩下的方法几乎也全都是在辅助我们这几个重要的模块,也都有注释,还是那句看Demo!
Socket学习总结系列(二) -- CocoaAsyncSocket的更多相关文章
- Telegram学习解析系列(二):这我怎么给后台传输数据?
写在前面: 在iOS开发的过程中,有很多时候我们都在和数据打交道,最基本的就是数据的下载和上传了,估计很多很多的小伙伴都在用AFNetworking与后台数据打交道,可有没有想过,哪天AFNetwor ...
- Socket学习总结系列(一) -- IM & Socket
写在准备动手的时候: Socket通讯在iOS中也是很常见,自己最近也一直在学习Telegram这个开源项目,Telegram就是在Socket的基础上做的即时通讯,这个相信了解这个开源项目的也都知道 ...
- 小白学习Spark系列二:spark应用打包傻瓜式教程(IntelliJ+maven 和 pycharm+jar)
在做spark项目时,我们常常面临如何在本地将其打包,上传至装有spark服务器上运行的问题.下面是我在项目中尝试的两种方案,也踩了不少坑,两者相比,方案一比较简单,本博客提供的jar包适用于spar ...
- 学习CNN系列二:训练过程
卷积神经网络在本质上是一种输入到输出的映射,它能够学习大量的输入与输出之间的映射关系,而不需要任何输入和输出之间精确的数学表达式,只要用已知的模式对卷积神经网络加以训练,网络就具有输入.输出之间映射的 ...
- Dubbo源码学习总结系列二 dubbo-rpc远程调用模块
dubbo本质是一个RPC框架,我们首先讨论这个骨干中的骨干,dubbo-rpc模块. 主要讨论一下几部分内容: 一.此模块在dubbo整体框架中的作用: 二.此模块需要完成的需求功能点及接口定义: ...
- JNI 学习笔记系列(二)
c中没有Boolean类型的值,一般是使用1表示true,0表示false,c中也没有String类型的数据,c中的字符串要通过char数组来表示.c中没有byte类型,一般用char表示byte类型 ...
- Windows-universal-samples学习笔记系列二:Controls, layout, and text
Controls, layout, and text AutoSuggestBox migration Clipboard Commanding Context menu Context menu ( ...
- 步步为营 SharePoint 开发学习笔记系列总结
转:http://www.cnblogs.com/springyangwc/archive/2011/08/03/2126763.html 概要 为时20多天的sharepoint开发学习笔记系列终于 ...
- MyBatis学习系列二——增删改查
目录 MyBatis学习系列一之环境搭建 MyBatis学习系列二——增删改查 MyBatis学习系列三——结合Spring 数据库的经典操作:增删改查. 在这一章我们主要说明一下简单的查询和增删改, ...
随机推荐
- linux新手入门前知道的一些概念
前言: 这篇文章是结合自己从小白开始学linux到工作中运用linux系统,以新手怎么理解的角度来说说linux系统,希望能给想学习linux的新手带来一些帮助. 引子:随着互联网技术不断更新,企业对 ...
- seajs源码阅读
乘着周日有点时间,阅读一下玉伯大神的源码. seajs的源码写得真的很好,很是佩服,工整美观不愧是大神,造福百姓. 说起seajs不得不说,AMD和CMD的区别. CMD 推崇依赖就近,AMD 推崇 ...
- H3CNE实验:配置交换机接口
第1步:配置交换机端口 <H3C>system-view System View: return to User View with Ctrl+Z. [H3C]interface Giga ...
- Vijos 1040 高精度乘法
描述 高精度乘法 输入:两行,每行表示一个非负整数(不超过10000位) 输出:两数的乘积. 样例1 样例输入1 99 101 样例输出1 9999 题解 这道题和之前的Vijos 1010 清帝之惑 ...
- angular.js ng-repeat渲染时出现闪烁问题解决
当我们前端运用到angular.js框架时,想必大家都会遇到一些坑.其中,我也来分享一个常见的angular.js渲染时出现的坑. 当我们进行页面渲染时,绑定表达式最开始会用{{data.name}} ...
- javascript编程代码笔记
1. 快速排序算法 方法一 function quicksort(n,left,right){ var p; if(left<right){ p = position(n,left,right) ...
- HDU 1325,POJ 1308 Is It A Tree
HDU认为1>2,3>2不是树,POJ认为是,而Virtual Judge上引用的是POJ数据这就是唯一的区别....(因为这个瞎折腾了半天) 此题因为是为了熟悉并查集而刷,其实想了下其实 ...
- 单片机C语言基础编程源码六则2
1.某单片机系统的P2口接一数模转换器DAC0832输出模拟量,现在要求从DAC0832输出连续的三角波,实现的方法是从P2口连续输出按照三角波变化的数值,从0开始逐渐增大,到某一最大值后逐渐减小,直 ...
- gtest 操作指南
首先,下载gtest-17.0,CSDN上就可以免费下载. 然后,打开gtest-17.0下的msvc文件夹,运行gtest.sln,右键解决方案,选择生成解决方案,此时会在gtest-17.0/ms ...
- c# 多线程 创建对象实例
本次的标题是我在写单例模式的博客时遇到的问题,所以今天专门写了的demo让自己记住怎么简单的使用多线程. 一直纠结的是怎么在for循环中多次实例化对象,好复现单例模式在没有加锁的情况下出现多个实例对象 ...