GCDAsyncSocket源码分析
GCDAsyncSocket.m的成员变量GCDAsyncSocket的初始化创建
GCDAsyncSocket Connect
GCDAsyncSocket.m的成员变量
@implementation GCDAsyncSocket
{
uint32_t flags
;
uint16_t config
;
__weak id
<GCDAsyncSocketDelegate
> delegate
;
dispatch_queue_t delegateQueue
;
int socket4FD
;
int socket6FD
;
int socketUN
;
NSURL
*socketUrl
;
int stateIndex
;
NSData
* connectInterface4
;
NSData
* connectInterface6
;
NSData
* connectInterfaceUN
;
dispatch_queue_t socketQueue
;
dispatch_source_t accept4Source
;
dispatch_source_t accept6Source
;
dispatch_source_t acceptUNSource
;
dispatch_source_t connectTimer
;
dispatch_source_t readSource
;
dispatch_source_t writeSource
;
dispatch_source_t readTimer
;
dispatch_source_t writeTimer
;
NSMutableArray
*readQueue
;
NSMutableArray
*writeQueue
;
GCDAsyncReadPacket
*currentRead
;
GCDAsyncWritePacket
*currentWrite
;
unsigned long socketFDBytesAvailable
;
GCDAsyncSocketPreBuffer
*preBuffer
;
#if TARGET_OS_IPHONE
CFStreamClientContext streamContext
;
CFReadStreamRef readStream
;
CFWriteStreamRef writeStream
;
#endif
SSLContextRef sslContext
;
GCDAsyncSocketPreBuffer
*sslPreBuffer
;
size_t sslWriteCachedLength
;
OSStatus sslErrCode
;
OSStatus lastSSLHandshakeError
;
void *IsOnSocketQueueOrTargetQueueKey
;
id userData
;
NSTimeInterval alternateAddressDelay
;
}
GCDAsyncSocket的初始化
创建
_Socket
= [[GCDAsyncSocket alloc
] initWithDelegate
:self delegateQueue
:dispatch_get_main_queue()];
- (instancetype
)initWithDelegate
:(id
<GCDAsyncSocketDelegate
>)aDelegate delegateQueue
:(dispatch_queue_t
)dq
{
return [self initWithDelegate
:aDelegate delegateQueue
:dq socketQueue
:NULL];
}
init方法最终将会来到,在这个方法里,同时socketQueue传值为NULL
- (instancetype
)initWithDelegate
:(id
<GCDAsyncSocketDelegate
>)aDelegate delegateQueue
:(dispatch_queue_t
)dq socketQueue
:(dispatch_queue_t
)sq
{
if((self
= [super init
]))
{
delegate
= aDelegate
;
delegateQueue
= dq
;
#if !OS_OBJECT_USE_OBJC
if (dq
) dispatch_retain(dq
);
#endif
socket4FD
= SOCKET_NULL
;
socket6FD
= SOCKET_NULL
;
socketUN
= SOCKET_NULL
;
socketUrl
= nil
;
stateIndex
= 0;
if (sq
)
{
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.");
socketQueue
= sq
;
#if !OS_OBJECT_USE_OBJC
dispatch_retain(sq
);
#endif
}
else
{
socketQueue
= dispatch_queue_create([GCDAsyncSocketQueueName UTF8String
], NULL);
}
IsOnSocketQueueOrTargetQueueKey
= &IsOnSocketQueueOrTargetQueueKey
;
void *nonNullUnusedPointer
= (__bridge
void *)self
;
dispatch_queue_set_specific(socketQueue
, IsOnSocketQueueOrTargetQueueKey
, nonNullUnusedPointer
, NULL);
readQueue
= [[NSMutableArray alloc
] initWithCapacity
:5];
currentRead
= nil
;
writeQueue
= [[NSMutableArray alloc
] initWithCapacity
:5];
currentWrite
= nil
;
preBuffer
= [[GCDAsyncSocketPreBuffer alloc
] initWithCapacity
:(1024 * 4)];
alternateAddressDelay
= 0.3;
}
return self
;
}
GCDAsyncSocket Connect
[self
.cmdSocket connectToHost
:hostIP onPort
:[hostPort integerValue
] error
:&cmdError
]
- (BOOL
)connectToHost
:(NSString
*)host onPort
:(uint16_t)port error
:(NSError
**)errPtr
{
return [self connectToHost
:host onPort
:port withTimeout
:-1 error
:errPtr
];
}
```cpp
- (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
];
}
(BOOL
)connectToHost
:(NSString
*)inHost
onPort
:(uint16_t)port
viaInterface
:(NSString
*)inInterface
withTimeout
:(NSTimeInterval
)timeout
error
:(NSError
**)errPtr
{
LogTrace();
NSString
*host
= [inHost copy
];
NSString
*interface
= [inInterface copy
];
__block BOOL result
= NO
;
__block NSError
*preConnectErr
= nil
;
dispatch_block_t block
= ^{ @autoreleasepool
{
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_from_block
;
}
if (![self preConnectWithInterface
:interface error
:&preConnectErr
])
{
return_from_block
;
}
self
->flags
|= kSocketStarted
;
LogVerbose(@
"Dispatching DNS lookup...");
NSString
*hostCpy
= [host copy
];
int aStateIndex
= self
->stateIndex
;
__weak GCDAsyncSocket
*weakSelf
= self
;
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
;
NSMutableArray
*addresses
= [[self
class] lookupHost
:hostCpy port
:port error
:&lookupErr
];
__strong GCDAsyncSocket
*strongSelf
= weakSelf
;
if (strongSelf
== nil
) return_from_block
;
if (lookupErr
)
{
dispatch_async(strongSelf
->socketQueue
, ^{ @autoreleasepool
{
[strongSelf lookup
:aStateIndex didFail
:lookupErr
];
}});
}
else
{
NSData
*address4
= nil
;
NSData
*address6
= nil
;
for (NSData
*address in addresses
)
{
if (!address4
&& [[self
class] isIPv4Address
:address
])
{
address4
= address
;
}
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
;
}};
if (dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey
))
block();
else
dispatch_sync(socketQueue
, block
);
if (errPtr
) *errPtr
= preConnectErr
;
return result
;
}
[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");
NSAssert(address4
|| address6
, @
"Expected at least one valid address");
if (aStateIndex
!= stateIndex
)
{
LogInfo(@
"Ignoring lookupDidSucceed, already disconnected");
return;
}
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;
}
NSError
*err
= nil
;
if (![self connectWithAddress4
:address4 address6
:address6 error
:&err
])
{
[self closeWithError
:err
];
}
}
[self connectWithAddress4
:address4 address6
:address6 error
:&err
]
- (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
]);
BOOL preferIPv6
= (config
& kPreferIPv6
) ? YES
: NO
;
if (address4
)
{
LogVerbose(@
"Creating IPv4 socket");
socket4FD
= [self createSocket
:AF_INET connectInterface
:connectInterface4 errPtr
:errPtr
];
}
if (address6
)
{
LogVerbose(@
"Creating IPv6 socket");
socket6FD
= [self createSocket
:AF_INET6 connectInterface
:connectInterface6 errPtr
:errPtr
];
}
if (socket4FD
== SOCKET_NULL
&& socket6FD
== SOCKET_NULL
)
{
return NO
;
}
int socketFD
, alternateSocketFD
;
NSData
*address
, *alternateAddress
;
if ((preferIPv6
&& socket6FD
!= SOCKET_NULL
) || socket4FD
== SOCKET_NULL
)
{
socketFD
= socket6FD
;
alternateSocketFD
= socket4FD
;
address
= address6
;
alternateAddress
= address4
;
}
else
{
socketFD
= socket4FD
;
alternateSocketFD
= socket6FD
;
address
= address4
;
alternateAddress
= address6
;
}
int aStateIndex
= stateIndex
;
[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
;
}
socket6FD
= [self createSocket
:AF_INET6 connectInterface
:connectInterface6 errPtr
:errPtr
];
- (int)createSocket
:(int)family connectInterface
:(NSData
*)connectInterface errPtr
:(NSError
**)errPtr
{
int socketFD
= socket(family
, SOCK_STREAM
, 0);
if (socketFD
== SOCKET_NULL
)
{
if (errPtr
)
*errPtr
= [self errorWithErrno
:errno reason
:@
"Error in socket() function"];
return socketFD
;
}
if (![self bindSocket
:socketFD toInterface
:connectInterface error
:errPtr
])
{
[self closeSocket
:socketFD
];
return SOCKET_NULL
;
}
int nosigpipe
= 1;
setsockopt(socketFD
, SOL_SOCKET
, SO_NOSIGPIPE
, &nosigpipe
, sizeof(nosigpipe
));
return socketFD
;
}
[self connectSocket
:socketFD address
:address stateIndex
:aStateIndex
];
- (void)connectSocket
:(int)socketFD address
:(NSData
*)address stateIndex
:(int)aStateIndex
{
if (self
.isConnected
)
{
[self closeSocket
:socketFD
];
return;
}
__weak GCDAsyncSocket
*weakSelf
= self
;
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"
int result
= connect(socketFD
, (const struct sockaddr
*)[address bytes
], (socklen_t
)[address length
]);
int err
= errno
;
__strong GCDAsyncSocket
*strongSelf
= weakSelf
;
if (strongSelf
== nil
) return_from_block
;
dispatch_async(strongSelf
->socketQueue
, ^{ @autoreleasepool
{
if (strongSelf
.isConnected
)
{
[strongSelf closeSocket
:socketFD
];
return_from_block
;
}
if (result
== 0)
{
[self closeUnusedSocket
:socketFD
];
[strongSelf didConnect
:aStateIndex
];
}
else
{
[strongSelf closeSocket
:socketFD
];
if (strongSelf
.socket4FD
== SOCKET_NULL
&& strongSelf
.socket6FD
== SOCKET_NULL
)
{
NSError
*error
= [strongSelf errorWithErrno
:err reason
:@
"Error in connect() function"];
[strongSelf didNotConnect
:aStateIndex error
:error
];
}
}
}});
#pragma clang diagnostic pop
});
LogVerbose(@
"Connecting...");
}