前言
CocoaAsyncSocket是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的、强大的异步套接字库,向上封装出简单易用OC接口。省去了我们面向Socket以及数据流Stream等繁琐复杂的编程。
这个就是整个库的类,一个是基于TCP,一个基于UDP,这篇文章主要是介绍GCDAsyncSocket的源码
GCDAsyncSocket源码分析
socket的客户端和服务端流程大概如下,大多数情况下我们都是基于客户端开发的,所以我们分析的流程就按照客户端的流程走:
先来看GCDAsyncSocket这个类的属性
@implementation GCDAsyncSocket
{
//flags,当前正在做操作的标识符
uint32_t flags;
uint16_t config;
//代理
__weak id<GCDAsyncSocketDelegate> delegate;
//代理回调的queue
dispatch_queue_t delegateQueue;
//本地IPV4Socket
int socket4FD;
//本地IPV6Socket
int socket6FD;
//unix域的套接字 // 进程通讯 locahost VS 127.0.0.1
// socket
int socketUN;
//unix域 服务端 url
NSURL *socketUrl;
//状态Index
int stateIndex;
//本机的IPV4地址 --- 地址host interface
NSData * connectInterface4;
//本机的IPV6地址
NSData * connectInterface6;
//本机unix域地址
NSData * connectInterfaceUN;
//这个类的对Socket的操作都在这个queue中,串行
dispatch_queue_t socketQueue;
// 源 ---> mergdata get_data buffer tls ssl CFStream
// data
dispatch_source_t accept4Source;
dispatch_source_t accept6Source;
dispatch_source_t acceptUNSource;
//连接timer,GCD定时器 重连
dispatch_source_t connectTimer;
dispatch_source_t readSource;
dispatch_source_t writeSource;
dispatch_source_t readTimer;
dispatch_source_t writeTimer;
//读写数据包数组 类似queue,最大限制为5个包 - FIFO
NSMutableArray *readQueue;
NSMutableArray *writeQueue;
//当前正在读写数据包
GCDAsyncReadPacket *currentRead;
GCDAsyncWritePacket *currentWrite;
//当前socket未获取完的数据大小
unsigned long socketFDBytesAvailable;
//全局公用的提前缓冲区
GCDAsyncSocketPreBuffer *preBuffer;
#if TARGET_OS_IPHONE
CFStreamClientContext streamContext;
//读的数据流 ---- c
CFReadStreamRef readStream;
//写的数据流
CFWriteStreamRef writeStream;
#endif
//SSL上下文,用来做SSL认证
SSLContextRef sslContext;
//全局公用的SSL的提前缓冲区
GCDAsyncSocketPreBuffer *sslPreBuffer;
size_t sslWriteCachedLength;
//记录SSL读取数据错误
OSStatus sslErrCode;
//记录SSL握手的错误
OSStatus lastSSLHandshakeError;
//socket队列的标识key -- key - queue
void *IsOnSocketQueueOrTargetQueueKey;
id userData;
//连接备选服务端地址的延时 (另一个IPV4或IPV6)
NSTimeInterval alternateAddressDelay;
}
先来分析这些属性的作用
- flags:代表socket当前的状态
// flags的枚举
enum GCDAsyncSocketFlags
{
kSocketStarted = 1 << 0, // If set, socket has been started (accepting/connecting)
kConnected = 1 << 1, // If set, the socket is connected
kForbidReadsWrites = 1 << 2, // If set, no new reads or writes are allowed
kReadsPaused = 1 << 3, // If set, reads are paused due to possible timeout
kWritesPaused = 1 << 4, // If set, writes are paused due to possible timeout
kDisconnectAfterReads = 1 << 5, // If set, disconnect after no more reads are queued
kDisconnectAfterWrites = 1 << 6, // If set, disconnect after no more writes are queued
kSocketCanAcceptBytes = 1 << 7, // If set, we know socket can accept bytes. If unset, it's unknown.
kReadSourceSuspended = 1 << 8, // If set, the read source is suspended
kWriteSourceSuspended = 1 << 9, // If set, the write source is suspended
kQueuedTLS = 1 << 10, // If set, we've queued an upgrade to TLS
kStartingReadTLS = 1 << 11, // If set, we're waiting for TLS negotiation to complete
kStartingWriteTLS = 1 << 12, // If set, we're waiting for TLS negotiation to complete
kSocketSecure = 1 << 13, // If set, socket is using secure communication via SSL/TLS
kSocketHasReadEOF = 1 << 14, // If set, we have read EOF from socket
kReadStreamClosed = 1 << 15, // If set, we've read EOF plus prebuffer has been drained
kDealloc = 1 << 16, // If set, the socket is being deallocated
#if TARGET_OS_IPHONE
kAddedStreamsToRunLoop = 1 << 17, // If set, CFStreams have been added to listener thread
kUsingCFStreamForTLS = 1 << 18, // If set, we're forced to use CFStream instead of SecureTransport
kSecureSocketHasBytesAvailable = 1 << 19, // If set, CFReadStream has notified us of bytes available
#endif
};
- config:代表了socket协议状态
// config的枚举
enum GCDAsyncSocketConfig
{
kIPv4Disabled = 1 << 0, // If set, IPv4 is disabled
kIPv6Disabled = 1 << 1, // If set, IPv6 is disabled
kPreferIPv6 = 1 << 2, // If set, IPv6 is preferred over IPv4
// 双工 - 半双工
kAllowHalfDuplexConnection = 1 << 3, // If set, the socket will stay open even if the read stream closes
};
- delegate:代理属性,外界传入一个代理的时候这里就会保存,用于一些状态的回调
- socket创建成功
- socket关闭
- socket写入数据
- socket读取数据
- socket断开
//代理
__weak id<GCDAsyncSocketDelegate> delegate;
- delegateQueue:回调使用的队列
//代理回调的queue
dispatch_queue_t delegateQueue;
- 三种socket
- 第一种socket4FD,代表的是
ipv4类型的socket - 第二种socket6FD,代表的是
ipv6类型的socket - 第二种socketUN,这个是线程间通讯的socket,其他两种类型的socket也可以实现线程间通讯,不过这个这种socket不用走传输层,比较快捷,不过用的地方比较少
- 第一种socket4FD,代表的是
//本地IPV4Socket
int socket4FD;
//本地IPV6Socket
int socket6FD;
//unix域的套接字 // 进程通讯 locahost VS 127.0.0.1
// socket
int socketUN;
//unix域 服务端 url
NSURL *socketUrl;
//状态Index
int stateIndex;
//本机的IPV4地址 --- 地址host interface
NSData * connectInterface4;
//本机的IPV6地址
NSData * connectInterface6;
//本机unix域地址
NSData * connectInterfaceUN;
- 定义dispatch_source_t类型的对象,用于处理数据,这样写性能比较高
// 源 ---> mergdata get_data buffer tls ssl CFStream
// data
dispatch_source_t accept4Source;
dispatch_source_t accept6Source;
dispatch_source_t acceptUNSource;
//连接timer,GCD定时器 重连
dispatch_source_t connectTimer;
dispatch_source_t readSource;
dispatch_source_t writeSource;
dispatch_source_t readTimer;
dispatch_source_t writeTimer;
- 定义读写的数组,这样写可以保证每个数据的时序性
//读写数据包数组 类似queue,最大限制为5个包 - FIFO
NSMutableArray *readQueue;
NSMutableArray *writeQueue;
- alternateAddressDelay:连接备选地址的延时时间
//连接备选服务端地址的延时 (另一个IPV4或IPV6)
NSTimeInterval alternateAddressDelay;
使用流程
// 创建socket
if (self.socket == nil)
self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
// 连接socket
if (!self.socket.isConnected){
NSError *error;
[self.socket connectToHost:@"127.0.0.1" onPort:8090 withTimeout:-1 error:&error];
if (error) NSLog(@"%@",error);
}
initWithDelegate:delegateQueue:源码分析
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
{
return [self initWithDelegate:aDelegate delegateQueue:dq socketQueue:NULL];
}
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq
{
if((self = [super init]))
{
delegate = aDelegate;
delegateQueue = dq;
//这个宏是在sdk6.0之后才有的,如果是之前的,则OS_OBJECT_USE_OBJC为0,!0即执行if语句
//对6.0的适配,如果是6.0以下,则去retain release,6.0之后ARC也管理了GCD
#if !OS_OBJECT_USE_OBJC
if (dq) dispatch_retain(dq);
#endif
//创建socket,先都置为 -1
//本机的ipv4
socket4FD = SOCKET_NULL;
//ipv6
socket6FD = SOCKET_NULL;
//应该是UnixSocket
socketUN = SOCKET_NULL;
//url
socketUrl = nil;
//状态
stateIndex = 0;
if (sq)
{
//如果scoketQueue是global的,则报错。断言必须要一个非并行queue。
NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
@"The given socketQueue parameter must not be a concurrent queue.");
NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
@"The given socketQueue parameter must not be a concurrent queue.");
NSAssert(sq != dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
@"The given socketQueue parameter must not be a concurrent queue.");
//拿到scoketQueue
socketQueue = sq;
//iOS6之下retain
#if !OS_OBJECT_USE_OBJC
dispatch_retain(sq);
#endif
}
else
{
//没有的话创建一个, 名字为:GCDAsyncSocket,串行
socketQueue = dispatch_queue_create([GCDAsyncSocketQueueName UTF8String], NULL);
}
//自己的指针等于自己原来的指针,成二级指针了 看了注释是为了以后省略&,让代码更可读?
IsOnSocketQueueOrTargetQueueKey = &IsOnSocketQueueOrTargetQueueKey;
void *nonNullUnusedPointer = (__bridge void *)self;
//dispatch_queue_set_specific给当前队里加一个标识 dispatch_get_specific当前线程取出这个标识,判断是不是在这个队列
//这个key的值其实就是一个一级指针的地址 ,第三个参数把自己传过去了,上下文对象?第4个参数,为销毁的时候用的,可以指定一个函数
dispatch_queue_set_specific(socketQueue, IsOnSocketQueueOrTargetQueueKey, nonNullUnusedPointer, NULL);
//读的数组 限制为5
readQueue = [[NSMutableArray alloc] initWithCapacity:5];
currentRead = nil;
//写的数组,限制5
writeQueue = [[NSMutableArray alloc] initWithCapacity:5];
currentWrite = nil;
//设置大小为 4kb
preBuffer = [[GCDAsyncSocketPreBuffer alloc] initWithCapacity:(1024 * 4)];
#pragma mark alternateAddressDelay??
//交替地址延时?? wtf
alternateAddressDelay = 0.3;
}
return self;
}
initWithDelegate:delegateQueue函数比较简单,主要是做了一些初始化的工作
connectToHost:onPort:withTimeout:error:是连接socket的主要函数入口
- (BOOL)connectToHost:(NSString *)host
onPort:(uint16_t)port
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr
{
return [self connectToHost:host onPort:port viaInterface:nil withTimeout:timeout error:errPtr];
}
//多一个inInterface,本机地址
- (BOOL)connectToHost:(NSString *)inHost
onPort:(uint16_t)port
viaInterface:(NSString *)inInterface
withTimeout:(NSTimeInterval)timeout
error:(NSError **)errPtr
{
LogTrace();//{} ??有什么意义? -- 跟踪当前行为
// Just in case immutable objects were passed
//拿到host ,copy防止值被修改
NSString *host = [inHost copy];
//interface?接口?
NSString *interface = [inInterface copy];
//声明两个__block的
__block BOOL result = NO;
//error信息
__block NSError *preConnectErr = nil;
//gcdBlock ,都包裹在自动释放池中 :
// 1: 大量临时变量 connect : 重连
// 2: 自定义线程管理 : nsoperation
// 3: 非UI 命令 工具
dispatch_block_t block = ^{ @autoreleasepool {
// Check for problems with host parameter
if ([host length] == 0)
{
NSString *msg = @"Invalid host parameter (nil or \"\"). Should be a domain name or IP address string.";
preConnectErr = [self badParamError:msg];
// 其实就是return,大牛的代码真是充满逼格 - ret
// 异步性 return --> 函数 - 编译
// 宏定义 -- 预编译 -- return - 提前
return_from_block;
}
// Run through standard pre-connect checks
//一个前置的检查,如果没通过返回,这q个检查里,如果interface有值,则会将本机的IPV4 IPV6的 address设置上。
// 参数 : 指针 操作同一片内存空间
if (![self preConnectWithInterface:interface error:&preConnectErr])
{
return_from_block;
}
// We've made it past all the checks.
// It's time to start the connection process.
//flags 做或等运算。 flags标识为开始Socket连接
flags |= kSocketStarted;
//又是一个{}? 只是为了标记么?
LogVerbose(@"Dispatching DNS lookup...");
// It's possible that the given host parameter is actually a NSMutableString.
//很可能给我们的服务端的参数是一个可变字符串
// So we want to copy it now, within this block that will be executed synchronously.
//所以我们需要copy,在Block里同步的执行
// This way the asynchronous lookup block below doesn't have to worry about it changing.
//这种基于Block的异步查找,不需要担心它被改变
//copy,防止改变
NSString *hostCpy = [host copy];
//拿到状态
int aStateIndex = stateIndex;
__weak GCDAsyncSocket *weakSelf = self;
//全局Queue ---> 服务器
// client <---> server
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//异步执行
dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool {
//忽视循环引用
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wimplicit-retain-self"
//查找错误
NSError *lookupErr = nil;
//server地址数组(包含IPV4 IPV6的地址 sockaddr_in6、sockaddr_in类型)
NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr];
//strongSelf
__strong GCDAsyncSocket *strongSelf = weakSelf;
//完整Block安全形态,在加个if
if (strongSelf == nil) return_from_block;
//如果有错
if (lookupErr)
{
//用cocketQueue
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
//一些错误处理,清空一些数据等等
[strongSelf lookup:aStateIndex didFail:lookupErr];
}});
}
//正常
else
{
NSData *address4 = nil;
NSData *address6 = nil;
//遍历地址数组
for (NSData *address in addresses)
{
//判断address4为空,且address为IPV4
if (!address4 && [[self class] isIPv4Address:address])
{
address4 = address;
}
//判断address6为空,且address为IPV6
else if (!address6 && [[self class] isIPv6Address:address])
{
address6 = address;
}
}
//异步去发起连接 串行队列
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
}
#pragma clang diagnostic pop
}});
//开启连接超时
[self startConnectTimeout:timeout];
result = YES;
}};
//在socketQueue中执行这个Block
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey))
block();
//否则同步的调起这个queue去执行
else
dispatch_sync(socketQueue, block);
//如果有错误,赋值错误
if (errPtr) *errPtr = preConnectErr;
//把连接是否成功的result返回
return result;
}
拆分下函数,LogTrace()这个宏分两种情况
// 调试状态下
#define LogTrace() LogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD)
// 非调试状态
#define LogTrace() {}
如果把block里面的代码折叠起来就会发现其实很简单,所以我们接下来的重点就分析下block里面的代码
dispatch_block_t block = ^{ @autoreleasepool {
// Check for problems with host parameter
if ([host length] == 0)
{
NSString *msg = @"Invalid host parameter (nil or \"\"). Should be a domain name or IP address string.";
preConnectErr = [self badParamError:msg];
// 异步性 return --> 函数 - 编译
// 宏定义 -- 预编译 -- return - 提前
return_from_block;
}
// Run through standard pre-connect checks
//一个前置的检查,如果没通过返回,这q个检查里,如果interface有值,则会将本机的IPV4 IPV6的 address设置上。
// 参数 : 指针 操作同一片内存空间
if (![self preConnectWithInterface:interface error:&preConnectErr])
{
return_from_block;
}
// We've made it past all the checks.
// It's time to start the connection process.
//flags 做或等运算。 flags标识为开始Socket连接
flags |= kSocketStarted;
//又是一个{}? 只是为了标记么?
LogVerbose(@"Dispatching DNS lookup...");
// It's possible that the given host parameter is actually a NSMutableString.
//很可能给我们的服务端的参数是一个可变字符串
// So we want to copy it now, within this block that will be executed synchronously.
//所以我们需要copy,在Block里同步的执行
// This way the asynchronous lookup block below doesn't have to worry about it changing.
//这种基于Block的异步查找,不需要担心它被改变
//copy,防止改变
NSString *hostCpy = [host copy];
//拿到状态
int aStateIndex = stateIndex;
__weak GCDAsyncSocket *weakSelf = self;
//全局Queue ---> 服务器
// client <---> server
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//异步执行
dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool {
//忽视循环引用
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wimplicit-retain-self"
//查找错误
NSError *lookupErr = nil;
//server地址数组(包含IPV4 IPV6的地址 sockaddr_in6、sockaddr_in类型)
NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr];
//strongSelf
__strong GCDAsyncSocket *strongSelf = weakSelf;
//完整Block安全形态,在加个if
if (strongSelf == nil) return_from_block;
//如果有错
if (lookupErr)
{
//用cocketQueue
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
//一些错误处理,清空一些数据等等
[strongSelf lookup:aStateIndex didFail:lookupErr];
}});
}
//正常
else
{
NSData *address4 = nil;
NSData *address6 = nil;
//遍历地址数组
for (NSData *address in addresses)
{
//判断address4为空,且address为IPV4
if (!address4 && [[self class] isIPv4Address:address])
{
address4 = address;
}
//判断address6为空,且address为IPV6
else if (!address6 && [[self class] isIPv6Address:address])
{
address6 = address;
}
}
//异步去发起连接 串行队列
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
}
#pragma clang diagnostic pop
}});
//开启连接超时
[self startConnectTimeout:timeout];
result = YES;
}};
return_from_block宏定义,大牛为什么这里要定义一个新的宏去表示return,因为这样定义的话可以在预编译的时候就把return给编译进去,就不会出现block要退出的时候还没编译出来就过了,不过这样的情况比较少
/**
* Seeing a return statements within an inner block
* can sometimes be mistaken for a return point of the enclosing method.
* This makes inline blocks a bit easier to read.
**/
#define return_from_block return
preConnectWithInterface: error:函数的作用:前置检查,检测interface是否有值,有就赋值
//在连接之前的接口检查,一般我们传nil interface本机的IP 端口等等
- (BOOL)preConnectWithInterface:(NSString *)interface error:(NSError **)errPtr
{
// socket - connect
//先断言,如果当前的queue不是初始化quueue,直接报错
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//无代理
if (delegate == nil) // Must have delegate set
{
if (errPtr)
{
NSString *msg = @"Attempting to connect without a delegate. Set a delegate first.";
*errPtr = [self badConfigError:msg];
}
return NO;
}
//没有代理queue
if (delegateQueue == NULL) // Must have delegate queue set
{
if (errPtr)
{
NSString *msg = @"Attempting to connect without a delegate queue. Set a delegate queue first.";
*errPtr = [self badConfigError:msg];
}
return NO;
}
//当前不是非连接状态
if (![self isDisconnected]) // Must be disconnected
{
if (errPtr)
{
NSString *msg = @"Attempting to connect while connected or accepting connections. Disconnect first.";
*errPtr = [self badConfigError:msg];
}
return NO;
}
//判断是否支持IPV4 IPV6 &位与运算,因为枚举是用 左位移<<运算定义的,所以可以用来判断 config包不包含某个枚举。因为一个值可能包含好几个枚举值,所以这时候不能用==来判断,只能用&来判断
BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO;
BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO;
//是否都不支持
if (isIPv4Disabled && isIPv6Disabled) // Must have IPv4 or IPv6 enabled
{
if (errPtr)
{
NSString *msg = @"Both IPv4 and IPv6 have been disabled. Must enable at least one protocol first.";
*errPtr = [self badConfigError:msg];
}
return NO;
}
//如果有interface,本机地址
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))
{
if (errPtr)
{
NSString *msg = @"Unknown interface. Specify valid interface by name (e.g. \"en1\") or IP address.";
*errPtr = [self badParamError:msg];
}
return NO;
}
if (isIPv4Disabled && (interface6 == nil))
{
if (errPtr)
{
NSString *msg = @"IPv4 has been disabled and specified interface doesn't support IPv6.";
*errPtr = [self badParamError:msg];
}
return NO;
}
if (isIPv6Disabled && (interface4 == nil))
{
if (errPtr)
{
NSString *msg = @"IPv6 has been disabled and specified interface doesn't support IPv4.";
*errPtr = [self badParamError:msg];
}
return NO;
}
//如果都没问题,则赋值
connectInterface4 = interface4;
connectInterface6 = interface6;
}
// Clear queues (spurious read/write requests post disconnect)
//清除queue(假的读写请求 ,提交断开连接)
//读写Queue清除
[readQueue removeAllObjects];
[writeQueue removeAllObjects];
return YES;
}
检测完之后获取全局并发队列异步去创建一个线程,
lookupHost:port:error:函数去获取服务器地址,通过递归的方式
//全局Queue ---> 服务器
// client <---> server
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//异步执行
dispatch_async(globalConcurrentQueue, ^{ @autoreleasepool {
//忽视循环引用
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wimplicit-retain-self"
//查找错误
NSError *lookupErr = nil;
//server地址数组(包含IPV4 IPV6的地址 sockaddr_in6、sockaddr_in类型)
NSMutableArray *addresses = [[self class] lookupHost:hostCpy port:port error:&lookupErr];
//strongSelf
__strong GCDAsyncSocket *strongSelf = weakSelf;
//完整Block安全形态,在加个if
if (strongSelf == nil) return_from_block;
//如果有错
if (lookupErr)
{
//用cocketQueue
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
//一些错误处理,清空一些数据等等
[strongSelf lookup:aStateIndex didFail:lookupErr];
}});
}
//正常
else
{
NSData *address4 = nil;
NSData *address6 = nil;
//遍历地址数组
for (NSData *address in addresses)
{
//判断address4为空,且address为IPV4
if (!address4 && [[self class] isIPv4Address:address])
{
address4 = address;
}
//判断address6为空,且address为IPV6
else if (!address6 && [[self class] isIPv6Address:address])
{
address6 = address;
}
}
//异步去发起连接 串行队列
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
}
#pragma clang diagnostic pop
}});
判断一下拿回来的地址是否有错,没错的话赋值到成员变量里面
//如果有错
if (lookupErr)
{
//用cocketQueue
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
//一些错误处理,清空一些数据等等
[strongSelf lookup:aStateIndex didFail:lookupErr];
}});
}
//正常
else
{
NSData *address4 = nil;
NSData *address6 = nil;
//遍历地址数组
for (NSData *address in addresses)
{
//判断address4为空,且address为IPV4
if (!address4 && [[self class] isIPv4Address:address])
{
address4 = address;
}
//判断address6为空,且address为IPV6
else if (!address6 && [[self class] isIPv6Address:address])
{
address6 = address;
}
}
//异步去发起连接 串行队列
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
}
异步串行队列去连接
//异步去发起连接 串行队列
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
[strongSelf lookup:aStateIndex didSucceedWithAddress4:address4 address6:address6];
}});
一系列的错误判断之后再去连接
- (void)lookup:(int)aStateIndex didSucceedWithAddress4:(NSData *)address4 address6:(NSData *)address6
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//至少有一个server地址
NSAssert(address4 || address6, @"Expected at least one valid address");
//如果状态不一致,说明断开连接
if (aStateIndex != stateIndex)
{
LogInfo(@"Ignoring lookupDidSucceed, already disconnected");
// The connect operation has been cancelled.
// That is, socket was disconnected, or connection has already timed out.
return;
}
// Check for problems
//分开判断。
BOOL isIPv4Disabled = (config & kIPv4Disabled) ? YES : NO;
BOOL isIPv6Disabled = (config & kIPv6Disabled) ? YES : NO;
if (isIPv4Disabled && (address6 == nil))
{
NSString *msg = @"IPv4 has been disabled and DNS lookup found no IPv6 address.";
[self closeWithError:[self otherError:msg]];
return;
}
if (isIPv6Disabled && (address4 == nil))
{
NSString *msg = @"IPv6 has been disabled and DNS lookup found no IPv4 address.";
[self closeWithError:[self otherError:msg]];
return;
}
// Start the normal connection process
NSError *err = nil;
//调用连接方法,如果失败,则错误返回
if (![self connectWithAddress4:address4 address6:address6 error:&err])
{
[self closeWithError:err];
}
}
connectWithAddress4: address6: error:函数是连接socket的入口,主要的功能是做一些连接前的判断
- (BOOL)connectWithAddress4:(NSData *)address4 address6:(NSData *)address6 error:(NSError **)errPtr
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//输出一些东西?
LogVerbose(@"IPv4: %@:%hu", [[self class] hostFromAddress:address4], [[self class] portFromAddress:address4]);
LogVerbose(@"IPv6: %@:%hu", [[self class] hostFromAddress:address6], [[self class] portFromAddress:address6]);
// Determine socket type
//判断是否倾向于IPV6
BOOL preferIPv6 = (config & kPreferIPv6) ? YES : NO;
// Create and bind the sockets
//如果有IPV4地址,创建IPV4 Socket
if (address4)
{
LogVerbose(@"Creating IPv4 socket");
socket4FD = [self createSocket:AF_INET connectInterface:connectInterface4 errPtr:errPtr];
}
//如果有IPV6地址,创建IPV6 Socket
if (address6)
{
LogVerbose(@"Creating IPv6 socket");
socket6FD = [self createSocket:AF_INET6 connectInterface:connectInterface6 errPtr:errPtr];
}
//如果都为空,直接返回
if (socket4FD == SOCKET_NULL && socket6FD == SOCKET_NULL)
{
return NO;
}
//主选socketFD,备选alternateSocketFD
int socketFD, alternateSocketFD;
//主选地址和备选地址
NSData *address, *alternateAddress;
//IPV6
if ((preferIPv6 && socket6FD) || socket4FD == SOCKET_NULL)
{
socketFD = socket6FD;
alternateSocketFD = socket4FD;
address = address6;
alternateAddress = address4;
}
//主选IPV4
else
{
socketFD = socket4FD;
alternateSocketFD = socket6FD;
address = address4;
alternateAddress = address6;
}
//拿到当前状态
int aStateIndex = stateIndex;
//用socket和address去连接
// socket & connect
[self connectSocket:socketFD address:address stateIndex:aStateIndex];
//如果有备选地址
if (alternateAddress)
{
//延迟去连接备选的地址
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(alternateAddressDelay * NSEC_PER_SEC)), socketQueue, ^{
[self connectSocket:alternateSocketFD address:alternateAddress stateIndex:aStateIndex];
});
}
return YES;
}
createSocket: connectInterface: errPtr:函数主要是创建一个socket
//创建Socket
- (int)createSocket:(int)family connectInterface:(NSData *)connectInterface errPtr:(NSError **)errPtr
{
//创建socket,用的SOCK_STREAM TCP流
int socketFD = socket(family, SOCK_STREAM, 0);
//如果创建失败
if (socketFD == SOCKET_NULL)
{
if (errPtr)
*errPtr = [self errnoErrorWithReason:@"Error in socket() function"];
return socketFD;
}
//和connectInterface绑定
if (![self bindSocket:socketFD toInterface:connectInterface error:errPtr])
{
//绑定失败,直接关闭返回
[self closeSocket:socketFD];
return SOCKET_NULL;
}
// Prevent SIGPIPE signals
//防止终止进程的信号?
int nosigpipe = 1;
//SO_NOSIGPIPE是为了避免网络错误,而导致进程退出。用这个来避免系统发送signal
setsockopt(socketFD, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe));
return socketFD;
}
connectSocket:address:stateIndex:函数创建一个全局并发队列去调用connect()函数去连接
//连接最终方法 3 finnal。。。
- (void)connectSocket:(int)socketFD address:(NSData *)address stateIndex:(int)aStateIndex
{
// If there already is a socket connected, we close socketFD and return
//已连接,关闭连接返回
if (self.isConnected)
{
[self closeSocket:socketFD];
return;
}
// Start the connection process in a background queue
//开始连接过程,在后台queue中
__weak GCDAsyncSocket *weakSelf = self;
//获取到全局Queue
dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//新线程
dispatch_async(globalConcurrentQueue, ^{
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wimplicit-retain-self"
//调用connect方法,该函数阻塞线程,所以要异步新线程
//客户端向特定网络地址的服务器发送连接请求,连接成功返回0,失败返回 -1。
int result = connect(socketFD, (const struct sockaddr *)[address bytes], (socklen_t)[address length]);
//老样子,安全判断
__strong GCDAsyncSocket *strongSelf = weakSelf;
if (strongSelf == nil) return_from_block;
//在socketQueue中,开辟线程
dispatch_async(strongSelf->socketQueue, ^{ @autoreleasepool {
//如果状态为已经连接,关闭连接返回
if (strongSelf.isConnected)
{
[strongSelf closeSocket:socketFD];
return_from_block;
}
//说明连接成功
if (result == 0)
{
//关闭掉另一个没用的socket
[self closeUnusedSocket:socketFD];
//调用didConnect,生成stream,改变状态等等!
[strongSelf didConnect:aStateIndex];
}
//连接失败
else
{
//关闭当前socket
[strongSelf closeSocket:socketFD];
// If there are no more sockets trying to connect, we inform the error to the delegate
//返回连接错误的error
if (strongSelf.socket4FD == SOCKET_NULL && strongSelf.socket6FD == SOCKET_NULL)
{
NSError *error = [strongSelf errnoErrorWithReason:@"Error in connect() function"];
[strongSelf didNotConnect:aStateIndex error:error];
}
}
}});
#pragma clang diagnostic pop
});
//输出正在连接中
LogVerbose(@"Connecting...");
}
如果连接成功,则改变当前socket的状态,调用
didConnect:方法
//连接成功后调用,设置一些连接成功的状态
- (void)didConnect:(int)aStateIndex
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//状态不同
if (aStateIndex != stateIndex)
{
LogInfo(@"Ignoring didConnect, already disconnected");
// The connect operation has been cancelled.
// That is, socket was disconnected, or connection has already timed out.
return;
}
//kConnected合并到当前flag中
flags |= kConnected;
//停止连接超时
[self endConnectTimeout];
#if TARGET_OS_IPHONE
// The endConnectTimeout method executed above incremented the stateIndex.
//上面的endConnectTimeout,会导致stateIndex增加,所以需要重新赋值
aStateIndex = stateIndex;
#endif
// Setup read/write streams (as workaround for specific shortcomings in the iOS platform)
//
// Note:
// There may be configuration options that must be set by the delegate before opening the streams.
//打开stream之前必须用相关配置设置代理
// The primary example is the kCFStreamNetworkServiceTypeVoIP flag, which only works on an unopened stream.
//主要的例子是kCFStreamNetworkServiceTypeVoIP标记,只能工作在未打开的stream中?
//
// Thus we wait until after the socket:didConnectToHost:port: delegate method has completed.
//所以我们要等待,连接完成的代理调用完
// This gives the delegate time to properly configure the streams if needed.
//这些给了代理时间,去正确的配置Stream,如果是必要的话
//创建个Block来初始化Stream
dispatch_block_t SetupStreamsPart1 = ^{
NSLog(@"hello~");
#if TARGET_OS_IPHONE
//创建读写stream失败,则关闭并报对应错误
if (![self createReadAndWriteStream])
{
[self closeWithError:[self otherError:@"Error creating CFStreams"]];
return;
}
//参数是给NO的,就是有可读bytes的时候,不会调用回调函数
if (![self registerForStreamCallbacksIncludingReadWrite:NO])
{
[self closeWithError:[self otherError:@"Error in CFStreamSetClient"]];
return;
}
#endif
};
//part2设置stream
dispatch_block_t SetupStreamsPart2 = ^{
#if TARGET_OS_IPHONE
//状态不一样直接返回
if (aStateIndex != stateIndex)
{
// The socket has been disconnected.
return;
}
//如果加到runloop上失败
if (![self addStreamsToRunLoop])
{
//错误返回
[self closeWithError:[self otherError:@"Error in CFStreamScheduleWithRunLoop"]];
return;
}
//读写stream open
if (![self openStreams])
{
//开启错误返回
[self closeWithError:[self otherError:@"Error creating CFStreams"]];
return;
}
#endif
};
// Notify delegate
//通知代理
//拿到server端的host port
NSString *host = [self connectedHost];
uint16_t port = [self connectedPort];
//拿到unix域的 url
NSURL *url = [self connectedUrl];
//拿到代理
__strong id theDelegate = delegate;
//代理队列 和 Host不为nil 且响应didConnectToHost代理方法
if (delegateQueue && host != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToHost:port:)])
{
//调用初始化stream1
SetupStreamsPart1();
dispatch_async(delegateQueue, ^{ @autoreleasepool {
//到代理队列调用连接成功的代理方法
[theDelegate socket:self didConnectToHost:host port:port];
//然后回到socketQueue中去执行初始化stream2
dispatch_async(socketQueue, ^{ @autoreleasepool {
SetupStreamsPart2();
}});
}});
}
//这个是unix domain 请求回调
else if (delegateQueue && url != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToUrl:)])
{
SetupStreamsPart1();
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate socket:self didConnectToUrl:url];
dispatch_async(socketQueue, ^{ @autoreleasepool {
SetupStreamsPart2();
}});
}});
}
//否则只初始化stream
else
{
SetupStreamsPart1();
SetupStreamsPart2();
}
// Get the connected socket
int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN;
//fcntl,功能描述:根据文件描述词来操作文件的特性。http://blog.csdn.net/pbymw8iwm/article/details/7974789
// Enable non-blocking IO on the socket
//使socket支持非阻塞IO
int result = fcntl(socketFD, F_SETFL, O_NONBLOCK);
if (result == -1)
{
//失败 ,报错
NSString *errMsg = @"Error enabling non-blocking IO on socket (fcntl)";
[self closeWithError:[self otherError:errMsg]];
return;
}
// Setup our read/write sources
//初始化读写source
[self setupReadAndWriteSourcesForNewlyConnectedSocket:socketFD];
// Dequeue any pending read/write requests
//开始下一个任务
[self maybeDequeueRead];
[self maybeDequeueWrite];
}
这个方法主要做了下面几件事:
- 把当前状态flags加上已连接,并且关闭掉我们一开始连接开启的,连接超时的定时器
//kConnected合并到当前flag中
flags |= kConnected;
//停止连接超时
[self endConnectTimeout];
- 初始化了两个Block:
SetupStreamsPart1、SetupStreamsPart2,这两个Block做的事都和读写流有关。SetupStreamsPart1用来创建读写流,并且注册回调。另一个SetupStreamsPart2用来把流添加到当前线程的runloop上,并且打开流。
//创建个Block来初始化Stream
dispatch_block_t SetupStreamsPart1 = ^{
...
};
//part2设置stream
dispatch_block_t SetupStreamsPart2 = ^{
...
};
- 判断是否有代理
queue、host或者url这些参数是否为空、是否代理响应didConnectToHost或didConnectToUrl代理,这两种分别对应了普通socket连接和unix domin socket连接。如果实现了对应的代理,则调用连接成功的代理。
//代理队列 和 Host不为nil 且响应didConnectToHost代理方法
if (delegateQueue && host != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToHost:port:)])
{
...
}
//这个是unix domain 请求回调
else if (delegateQueue && url != nil && [theDelegate respondsToSelector:@selector(socket:didConnectToUrl:)])
{
...
}
//否则只初始化stream
else
{
...
}
- 在调用代理的同时,调用了我们之前初始化的两个读写流相关的Block。
//调用初始化stream1
SetupStreamsPart1();
dispatch_async(delegateQueue, ^{ @autoreleasepool {
//到代理队列调用连接成功的代理方法
[theDelegate socket:self didConnectToHost:host port:port];
//然后回到socketQueue中去执行初始化stream2
dispatch_async(socketQueue, ^{ @autoreleasepool {
SetupStreamsPart2();
}});
}});
- 使socket支持非阻塞IO,默认是阻塞IO的
//使socket支持非阻塞IO
int result = fcntl(socketFD, F_SETFL, O_NONBLOCK);
- 初始化读写source
[self setupReadAndWriteSourcesForNewlyConnectedSocket:socketFD];
block(
SetupStreamsPart1)里面的代码createReadAndWriteStream()函数:创建读写stream
//创建读写stream
- (BOOL)createReadAndWriteStream
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//如果有一个有值,就返回
if (readStream || writeStream)
{
// Streams already created
return YES;
}
//拿到socket,首选是socket4FD,其次socket6FD,都没有才是socketUN,socketUN应该是Unix的socket结构体
int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN;
//如果都为空,返回NO
if (socketFD == SOCKET_NULL)
{
// Cannot create streams without a file descriptor
return NO;
}
//如果非连接,返回NO
if (![self isConnected])
{
// Cannot create streams until file descriptor is connected
return NO;
}
LogVerbose(@"Creating read and write stream...");
#pragma mark - 绑定Socket和CFStream
//下面的接口用于创建一对 socket stream,一个用于读取,一个用于写入:
CFStreamCreatePairWithSocket(NULL, (CFSocketNativeHandle)socketFD, &readStream, &writeStream);
// The kCFStreamPropertyShouldCloseNativeSocket property should be false by default (for our case).
// But let`s not take any chances.
//读写stream都设置成不会随着绑定的socket一起close,release。 kCFBooleanFalse不一起,kCFBooleanTrue一起
if (readStream)
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse);
if (writeStream)
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanFalse);
//如果有一个为空
if ((readStream == NULL) || (writeStream == NULL))
{
LogWarn(@"Unable to create read and write stream...");
//关闭对应的stream
if (readStream)
{
CFReadStreamClose(readStream);
CFRelease(readStream);
readStream = NULL;
}
if (writeStream)
{
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
writeStream = NULL;
}
//返回创建失败
return NO;
}
//创建成功
return YES;
}
block(
SetupStreamsPart1)里面的代码registerForStreamCallbacksIncludingReadWrite()函数:注册stream
//注册Stream的回调
- (BOOL)registerForStreamCallbacksIncludingReadWrite:(BOOL)includeReadWrite
{
LogVerbose(@"%@ %@", THIS_METHOD, (includeReadWrite ? @"YES" : @"NO"));
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//判断读写stream是不是都为空
NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null");
//客户端stream上下文对象
streamContext.version = 0;
streamContext.info = (__bridge void *)(self);
streamContext.retain = nil;
streamContext.release = nil;
streamContext.copyDescription = nil;
// The open has completed successfully.
// The stream has bytes to be read.
// The stream can accept bytes for writing.
// An error has occurred on the stream.
// The end of the stream has been reached.
//设置一个CF的flag 两种,一种是错误发生的时候,一种是stream事件结束
CFOptionFlags readStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered ;
//如果包含读写
if (includeReadWrite)
//仍然有Bytes要读的时候 The stream has bytes to be read.
readStreamEvents |= kCFStreamEventHasBytesAvailable;
//给读stream设置客户端,会在之前设置的那些标记下回调函数 CFReadStreamCallback。设置失败的话直接返回NO
if (!CFReadStreamSetClient(readStream, readStreamEvents, &CFReadStreamCallback, &streamContext))
{
return NO;
}
//写的flag,也一样
CFOptionFlags writeStreamEvents = kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered;
if (includeReadWrite)
writeStreamEvents |= kCFStreamEventCanAcceptBytes;
if (!CFWriteStreamSetClient(writeStream, writeStreamEvents, &CFWriteStreamCallback, &streamContext))
{
return NO;
}
//走到最后说明读写都设置回调成功,返回YES
return YES;
}
block(
SetupStreamsPart2)里面的代码addStreamsToRunLoop函数:把stream添加到runloop上
//把stream添加到runloop上
- (BOOL)addStreamsToRunLoop
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null");
//判断flag里是否包含kAddedStreamsToRunLoop,没添加过则添加。
if (!(flags & kAddedStreamsToRunLoop))
{
LogVerbose(@"Adding streams to runloop...");
[[self class] startCFStreamThreadIfNeeded];
//在开启的线程中去执行,阻塞式的
[[self class] performSelector:@selector(scheduleCFStreams:)
onThread:cfstreamThread
withObject:self
waitUntilDone:YES];
//添加标识
flags |= kAddedStreamsToRunLoop;
}
return YES;
}
block(
SetupStreamsPart2)里面的代码openStreams函数:打开stream
//打开stream
- (BOOL)openStreams
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//断言读写stream都不会空
NSAssert((readStream != NULL && writeStream != NULL), @"Read/Write stream is null");
//返回stream的状态
/*
kCFStreamStatusNotOpen = 0,
kCFStreamStatusOpening, // open is in-progress
kCFStreamStatusOpen,
kCFStreamStatusReading,
kCFStreamStatusWriting,
kCFStreamStatusAtEnd, // no further bytes can be read/written
kCFStreamStatusClosed,
kCFStreamStatusError
*/
CFStreamStatus readStatus = CFReadStreamGetStatus(readStream);
CFStreamStatus writeStatus = CFWriteStreamGetStatus(writeStream);
//如果有任意一个没有开启
if ((readStatus == kCFStreamStatusNotOpen) || (writeStatus == kCFStreamStatusNotOpen))
{
LogVerbose(@"Opening read and write stream...");
//开启 - NSStream
BOOL r1 = CFReadStreamOpen(readStream);
BOOL r2 = CFWriteStreamOpen(writeStream);
//有一个开启失败
if (!r1 || !r2)
{
LogError(@"Error in CFStreamOpen");
return NO;
}
}
return YES;
}
注册stream的回调函数中,先看
CFReadStreamCallback,读取到数据的时候会调用doReadData函数
//读stream的回调
static void CFReadStreamCallback (CFReadStreamRef stream, CFStreamEventType type, void *pInfo)
{
//得到触发回调的sokcet
GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo;
switch(type)
{
//如果是可读数据的回调
case kCFStreamEventHasBytesAvailable:
{
//在socketQueue中调用
dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool {
LogCVerbose(@"CFReadStreamCallback - HasBytesAvailable");
//如果不是同一个stream,直接返回
if (asyncSocket->readStream != stream)
return_from_block;
//如果包含正在初始化TLS,就先去握手再说
if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS))
{
// If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie.
// (A callback related to the tcp stream, but not to the SSL layer).
if (CFReadStreamHasBytesAvailable(asyncSocket->readStream))
{
asyncSocket->flags |= kSecureSocketHasBytesAvailable;
[asyncSocket cf_finishSSLHandshake];
}
}
//去读取数据
else
{
asyncSocket->flags |= kSecureSocketHasBytesAvailable;
[asyncSocket doReadData];
}
}});
break;
}
//这是错误的回调
default:
{
//得到错误
NSError *error = (__bridge_transfer NSError *)CFReadStreamCopyError(stream);
//到达流尾的错误
if (error == nil && type == kCFStreamEventEndEncountered)
{
error = [asyncSocket connectionClosedError];
}
dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool {
LogCVerbose(@"CFReadStreamCallback - Other");
if (asyncSocket->readStream != stream)
return_from_block;
//如果当前是正在进行SSL认证
if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS))
{
//则关闭ssl,报错
[asyncSocket cf_abortSSLHandshake:error];
}
else
{
//直接关闭
[asyncSocket closeWithError:error];
}
}});
break;
}
}
}
doReadData函数这里面会处理读取出来的数据,然后把调用completeCurrentRead方法把数据用代理传出去
//读取数据
- (void)doReadData
{
...
if (done)
{
//完成这次数据的读取
[self completeCurrentRead];
//如果没出错,没有到边界,prebuffer中还有可读数据
if (!error && (!socketEOF || [preBuffer availableBytes] > 0))
{
//让读操作离队,继续进行下一次读取
[self maybeDequeueRead];
}
}
...
}
//完成了这次的读数据
- (void)completeCurrentRead
{
LogTrace();
//断言currentRead
NSAssert(currentRead, @"Trying to complete current read when there is no current read.");
//结果数据
NSData *result = nil;
//如果是我们自己创建的Buffer
if (currentRead->bufferOwner)
{
// We created the buffer on behalf of the user.
// Trim our buffer to be the proper size.
//修剪buffer到合适的大小
//把大小设置到我们读取到的大小
[currentRead->buffer setLength:currentRead->bytesDone];
//赋值给result
result = currentRead->buffer;
}
else
{
// We did NOT create the buffer.
// The buffer is owned by the caller.
// Only trim the buffer if we had to increase its size.
//这是调用者的data,我们只会去加大尺寸
if ([currentRead->buffer length] > currentRead->originalBufferLength)
{
//拿到的读的size
NSUInteger readSize = currentRead->startOffset + currentRead->bytesDone;
//拿到原始尺寸
NSUInteger origSize = currentRead->originalBufferLength;
//取得最大的
NSUInteger buffSize = MAX(readSize, origSize);
//把buffer设置为较大的尺寸
[currentRead->buffer setLength:buffSize];
}
//拿到数据的头指针
uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset;
//reslut为,从头指针开始到长度为写的长度 freeWhenDone为YES,创建完就释放buffer
result = [NSData dataWithBytesNoCopy:buffer length:currentRead->bytesDone freeWhenDone:NO];
}
__strong id theDelegate = delegate;
#pragma mark -总算到调用代理方法,接受到数据了
if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadData:withTag:)])
{
//拿到当前的数据包
GCDAsyncReadPacket *theRead = currentRead; // Ensure currentRead retained since result may not own buffer
dispatch_async(delegateQueue, ^{ @autoreleasepool {
//把result在代理queue中回调出去。
[theDelegate socket:self didReadData:result withTag:theRead->tag];
}});
}
//取消掉读取超时
[self endCurrentRead];
}
CFWriteStreamCallback中的处理方式和CFReadStreamCallback差不多
//写的回调
static void CFWriteStreamCallback (CFWriteStreamRef stream, CFStreamEventType type, void *pInfo)
{
GCDAsyncSocket *asyncSocket = (__bridge GCDAsyncSocket *)pInfo;
switch(type)
{
//如果可写字节
case kCFStreamEventCanAcceptBytes:
{
dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool {
LogCVerbose(@"CFWriteStreamCallback - CanAcceptBytes");
if (asyncSocket->writeStream != stream)
return_from_block;
if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS))
{
// If we set kCFStreamPropertySSLSettings before we opened the streams, this might be a lie.
// (A callback related to the tcp stream, but not to the SSL layer).
//判断当前sokcet是否可以写数据,而不被阻塞
if (CFWriteStreamCanAcceptBytes(asyncSocket->writeStream))
{
asyncSocket->flags |= kSocketCanAcceptBytes;
[asyncSocket cf_finishSSLHandshake];
}
}
else
{
asyncSocket->flags |= kSocketCanAcceptBytes;
[asyncSocket doWriteData];
}
}});
break;
}
default:
{
NSError *error = (__bridge_transfer NSError *)CFWriteStreamCopyError(stream);
if (error == nil && type == kCFStreamEventEndEncountered)
{
error = [asyncSocket connectionClosedError];
}
dispatch_async(asyncSocket->socketQueue, ^{ @autoreleasepool {
LogCVerbose(@"CFWriteStreamCallback - Other");
if (asyncSocket->writeStream != stream)
return_from_block;
if ((asyncSocket->flags & kStartingReadTLS) && (asyncSocket->flags & kStartingWriteTLS))
{
[asyncSocket cf_abortSSLHandshake:error];
}
else
{
[asyncSocket closeWithError:error];
}
}});
break;
}
}
}
调用
doWriteData函数去执行写入的操作,处理完成后调用completeCurrentWrite把数据回调出去
//开始写数据 ,当前任务的
- (void)doWriteData
{
...
//如果完成了
if (done)
{
//完成操作
[self completeCurrentWrite];
if (!error)
{
dispatch_async(socketQueue, ^{ @autoreleasepool{
//开始下一次的读取任务
[self maybeDequeueWrite];
}});
}
}
...
}
//完成了当前的写
- (void)completeCurrentWrite
{
LogTrace();
NSAssert(currentWrite, @"Trying to complete current write when there is no current write.");
__strong id theDelegate = delegate;
if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWriteDataWithTag:)])
{
long theWriteTag = currentWrite->tag;
//调用完成写的回调
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate socket:self didWriteDataWithTag:theWriteTag];
}});
}
//关闭当前写
[self endCurrentWrite];
}
参考文章
iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Connect篇)
iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Connect篇终)
iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Read篇)
iOS即时通讯进阶 - CocoaAsyncSocket源码解析(Read篇终)