因为上一篇文章篇幅过长移到这边。数组
//写数据对外方法
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if ([data length] == 0) return;
//初始化写包,可知write操做是用GCDAsyncWritePacket这种包的形式发送的,若是没错的话,read应该也是这样吧。
GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
//异步
dispatch_async(socketQueue, ^{ @autoreleasepool {
LogTrace();
if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
{
//writeQueue是一个数组,把当前packet添加进去
[writeQueue addObject:packet];
//还没看到write(),得继续跳转
[self maybeDequeueWrite];
}
}});
// Do not rely on the block being run in order to release the packet,
// as the queue might get released without the block completing.
}
复制代码
这个方法只是构建了一个GCDAsyncWritePacket包,添加到writequeue数组中,而后继续调用别的方法安全
- (void)maybeDequeueWrite
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
// If we're not currently processing a write AND we have an available write stream //currentWrite也是一个GCDAsyncWritePacket包 //若是currentWrite为空,且socket已经链接 if ((currentWrite == nil) && (flags & kConnected)) { //若是用来写的数组数量大于0 if ([writeQueue count] > 0) { // Dequeue the next object in the write queue //将第一个包赋值给currentWrite,并从数组中移除 currentWrite = [writeQueue objectAtIndex:0]; [writeQueue removeObjectAtIndex:0]; //TLS,若是是GCDAsyncSpecialPacket,通常不走这里 if ([currentWrite isKindOfClass:[GCDAsyncSpecialPacket class]]) { LogVerbose(@"Dequeued GCDAsyncSpecialPacket"); // Attempt to start TLS flags |= kStartingWriteTLS; // This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set
[self maybeStartTLS];
}
else
{
LogVerbose(@"Dequeued GCDAsyncWritePacket");
// Setup write timer (if needed)
//设置一个GCD timer 来记录超时时间
[self setupWriteTimerWithTimeout:currentWrite->timeout];
// 开始write
[self doWriteData];
}
}
// flags & kDisconnectAfterWrites 跟 数组小于等于0 的 意思差很少
else if (flags & kDisconnectAfterWrites)
{
//若是没有可读任务,直接关闭socket
if (flags & kDisconnectAfterReads)
{
if (([readQueue count] == 0) && (currentRead == nil))
{
[self closeWithError:nil];
}
}
else
{
[self closeWithError:nil];
}
}
}
}
复制代码
这个方法主要的就是给currentWrite赋值,writeQueue的数量判断,大于0就继续往下,小于等于0就断开链接。还作了一步[GCDAsyncSpecialPacket class]的判断,咱们write的时候,进来的是普通包,不是special包,基本上不会走进去,因此我那继续往下走就是执行[self doWriteData];
这又是一个很长的方法。这里面包含了不少flags判断操做,好比:socket安全等等,须要有设置才会执行bash
- (void)doWriteData
{
LogTrace();
//currentWrite为空,不写
if ((currentWrite == nil) || (flags & kWritesPaused))
{
LogVerbose(@"No currentWrite or kWritesPaused");
if ([self usingCFStreamForTLS])
{
//啥也不干
}
else
{
//若是socket中可接受写数据,防止反复触发写source,挂起
if (flags & kSocketCanAcceptBytes)
{
[self suspendWriteSource];
}
}
return;
}
//若是当前socket没法在写数据了
if (!(flags & kSocketCanAcceptBytes))
{
LogVerbose(@"No space available to write...");
// No space available to write.
//若是不是cfstream
if (![self usingCFStreamForTLS])
{
// Need to wait for writeSource to fire and notify us of
// available space in the socket's internal write buffer. //则恢复写source,当有空间去写的时候,会触发回来 [self resumeWriteSource]; } return; } //若是正在进行TLS认证,就是那个specialpacket,咱们当前不是这个因此先跳过 if (flags & kStartingWriteTLS) { LogVerbose(@"Waiting for SSL/TLS handshake to complete"); // The writeQueue is waiting for SSL/TLS handshake to complete. if (flags & kStartingReadTLS) { //若是是安全通道,而且I/O阻塞,那么从新去握手 if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) { // We are in the process of a SSL Handshake. // We were waiting for available space in the socket's internal OS buffer to continue writing.
[self ssl_continueSSLHandshake];
}
}
//说明不走`TLS`了,由于只支持写的TLS
else
{
// We are still waiting for the readQueue to drain and start the SSL/TLS process.
// We now know we can write to the socket.
//挂起写source
if (![self usingCFStreamForTLS])
{
// Suspend the write source or else it will continue to fire nonstop.
[self suspendWriteSource];
}
}
return;
}
// Note: This method is not called if currentWrite is a GCDAsyncSpecialPacket (startTLS packet)
//开始写数据
BOOL waiting = NO;
NSError *error = nil;
size_t bytesWritten = 0;
//安全链接
if (flags & kSocketSecure)
{
//这里先省略,关键看普通链接。有须要仔细了解的能够私信我。我给你发代码- -
...
}
//普通socket
else
{
//拿到当前socket
int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN;
//获得指针偏移
const uint8_t *buffer = (const uint8_t *)[currentWrite->buffer bytes] + currentWrite->bytesDone;
// 要写的数据大小
NSUInteger bytesToWrite = [currentWrite->buffer length] - currentWrite->bytesDone;
if (bytesToWrite > SIZE_MAX) // NSUInteger may be bigger than size_t (write param 3)
{
bytesToWrite = SIZE_MAX;
}
//直接写,熟悉的write()
ssize_t result = write(socketFD, buffer, (size_t)bytesToWrite);
LogVerbose(@"wrote to socket = %zd", result);
// 结果判断
if (result < 0)
{
//IO阻塞
if (errno == EWOULDBLOCK)
{
waiting = YES;
}
else
{
error = [self errnoErrorWithReason:@"Error in write() function"];
}
}
else
{
//获得写的大小
bytesWritten = result;
}
}
//注意,若是用CFStream,极可能会被恶意的放置数据 阻塞socket
//若是等待,则恢复写source
if (waiting)
{
//把socket可接受数据的标记去掉
flags &= ~kSocketCanAcceptBytes;
if (![self usingCFStreamForTLS])
{
//恢复写source
[self resumeWriteSource];
}
}
// Check our results
//判断是否完成
BOOL done = NO;
//判断已写大小
if (bytesWritten > 0)
{
// Update total amount read for the current write
//更新当前总共写的大小
currentWrite->bytesDone += bytesWritten;
LogVerbose(@"currentWrite->bytesDone = %lu", (unsigned long)currentWrite->bytesDone);
// Is packet done?
//判断当前写包是否写完
done = (currentWrite->bytesDone == [currentWrite->buffer length]);
}
//若是完成了
if (done)
{
//完成操做,回调发送成功代理事件,注意,代理回调是这里
[self completeCurrentWrite];
if (!error)
{
dispatch_async(socketQueue, ^{ @autoreleasepool{
//开始下一次的读取任务
[self maybeDequeueWrite];
}});
}
}
//未完成
else
{
//若是不是等待 并且没有出错
if (!waiting && !error)
{
//这是咱们写了一部分数据的状况。
//去掉可接受数据的标记
flags &= ~kSocketCanAcceptBytes;
//再去等读source触发
if (![self usingCFStreamForTLS])
{
[self resumeWriteSource];
}
}
//若是已写大于0
if (bytesWritten > 0)
{
__strong id theDelegate = delegate;
//调用写的进度代理
if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didWritePartialDataOfLength:tag:)])
{
long theWriteTag = currentWrite->tag;
dispatch_async(delegateQueue, ^{ @autoreleasepool {
[theDelegate socket:self didWritePartialDataOfLength:bytesWritten tag:theWriteTag];
}});
}
}
}
// Check for errors
//若是有错,则报错断开链接
if (error)
{
[self closeWithError:[self errnoErrorWithReason:@"Error in write() function"]];
}
}
复制代码
这个方法贼长,长的主要缘由仍是TSL,SSL这两个安全传输协议的处理,咱们这里就先看看普通的吧...那些实在很长,注释也不少。 接下来看read,看看会发现,方法格式差很少app
- (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包,而后添加到数组中,而后执行下一个方法less
- (void)maybeDequeueRead
{
LogTrace();
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//若是当前读的包为空,并且flag为已链接
if ((currentRead == nil) && (flags & kConnected))
{
//若是读的queue大于0 (里面装的是咱们封装的GCDAsyncReadPacket数据包)
if ([readQueue count] > 0)
{
// Dequeue the next object in the write queue
//使得下一个对象从写的queue中离开
//从readQueue中拿到第一个写的数据
currentRead = [readQueue objectAtIndex:0];
//移除
[readQueue removeObjectAtIndex:0];
//咱们的数据包,若是是GCDAsyncSpecialPacket这种类型,这个包里装了TLS的一些设置
//若是是这种类型的数据,那么咱们就进行TLS
if ([currentRead isKindOfClass:[GCDAsyncSpecialPacket class]])
{
LogVerbose(@"Dequeued GCDAsyncSpecialPacket");
// Attempt to start TLS
//标记flag为正在读取TLS
flags |= kStartingReadTLS;
// This method won't do anything unless both kStartingReadTLS and kStartingWriteTLS are set //只有读写都开启了TLS,才会作TLS认证 [self maybeStartTLS]; } else { LogVerbose(@"Dequeued GCDAsyncReadPacket"); // Setup read timer (if needed) //设置读的任务超时,每次延时的时候还会调用 [self doReadData]; [self setupReadTimerWithTimeout:currentRead->timeout]; // Immediately read, if possible //读取数据 [self doReadData]; } } //读的队列没有数据,标记flag为,读了没有数据则断开链接状态 else if (flags & kDisconnectAfterReads) { //若是标记有写而后断开链接 if (flags & kDisconnectAfterWrites) { //若是写的队列为0,并且写为空 if (([writeQueue count] == 0) && (currentWrite == nil)) { //断开链接 [self closeWithError:nil]; } } else { //断开链接 [self closeWithError:nil]; } } //若是有安全socket。 else if (flags & kSocketSecure) { [self flushSSLBuffers]; //若是可读字节数为0 if ([preBuffer availableBytes] == 0) { //CFStream形式TLS if ([self usingCFStreamForTLS]) { // Callbacks never disabled } else { //从新恢复读的source。由于每次开始读数据的时候,都会挂起读的source [self resumeReadSource]; } } } } } 复制代码
也是一系列的安全协议判断并最终走向[self doReadData];
这个方法比write还长,我以为有必要列一下这个方法里面都作了什么。。。dom
1.判断currentRead是否为空,为空就挂起readSource 2.判断TLS 3.从preBuffer区读取数据 4.从socket中读取数据 5.读取完代理回调 6.错误检查异步
- (void)doReadData
{
LogTrace();
//若是当前读取的包为空,或者flag为读取中止,这两种状况是不能去读取数据的
if ((currentRead == nil) || (flags & kReadsPaused))
{
LogVerbose(@"No currentRead or kReadsPaused");
// Unable to read at this time
//若是是安全的通讯,经过TLS/SSL
if (flags & kSocketSecure)
{
// Here's the situation: // 这有一个场景 // We have an established secure connection. //咱们有一个肯定的安全的链接 // There may not be a currentRead, but there might be encrypted data sitting around for us. //可能没有当即去读,可是或许已经有加密的数据闲置在那 // When the user does get around to issuing a read, that encrypted data will need to be decrypted. // 当用户开始进行一个read,这些加密的数据须要被解码 // So why make the user wait? //因此为何让用户等待? // We might as well get a head start on decrypting some data now. // 咱们最好能够先进行数据解密 // The other reason we do this has to do with detecting a socket disconnection. //另外的理由是,咱们作这些不得不去检测socket的断开链接 // The SSL/TLS protocol has it's own disconnection handshake.
//SSL/TLS协议有本身的断开链接的握手
// So when a secure socket is closed, a "goodbye" packet comes across the wire.
//因此当一个安全链接关闭,一个“goodbye"数据包会被发送在电报中 // We want to make sure we read the "goodbye" packet so we can properly detect the TCP disconnection. //咱们想要确保读到“goodbye”数据包,所以咱们能够肯定检测到TCP链接断开 //刷新SSLBuffer,把数据从链路上移到prebuffer中 (当前暂停的时候作) [self flushSSLBuffers]; } //判断是否用的是 CFStream的TLS if ([self usingCFStreamForTLS]) { // CFReadStream only fires once when there is available data. // It won't fire again until we've invoked CFReadStreamRead. //CFReadStream只会调起一次,当有可读的数据。 不会再次被调用,直到咱们唤醒CFReadStreamRead。 // source --> data --> stream } else { // If the readSource is firing, we need to pause it // or else it will continue to fire over and over again. // // If the readSource is not firing, // we want it to continue monitoring the socket. //若是读的source正在触发,咱们须要去中止它,不然它会持续的被触发一遍又一遍。(要等咱们把现有传过来的数据读完,才能触发下一次。) //若是读的source没有触发。咱们想要它继续去监视socket. //挂起source if (socketFDBytesAvailable > 0) { [self suspendReadSource]; } } return; } //当前数据包不为空或者flag不为kReadsPaused,正式开始读取数据 //声明是否可读,可读数据为多大 BOOL hasBytesAvailable = NO; unsigned long estimatedBytesAvailable = 0; //若是用了CFStream if ([self usingCFStreamForTLS]) { #if TARGET_OS_IPHONE // Requested CFStream, rather than SecureTransport, for TLS (via GCDAsyncSocketUseCFStreamForTLS) //不须要获得数据大小 estimatedBytesAvailable = 0; //判断若是状态可读并且有可读数据,hasBytesAvailable则为YES if ((flags & kSecureSocketHasBytesAvailable) && CFReadStreamHasBytesAvailable(readStream)) hasBytesAvailable = YES; else hasBytesAvailable = NO; #endif } else { //拿到当前读到的数据大小,安全通道的和普通socket数据都和 socketFDBytesAvailable 有关 estimatedBytesAvailable = socketFDBytesAvailable; //若是是安全socket if (flags & kSocketSecure) { // There are 2 buffers to be aware of here. // 这里有2个buffer须要知道,一个是sslPreBuffer还有一个是安全传输中未读取的buffer // We are using SecureTransport, a TLS/SSL security layer which sits atop TCP. //咱们使用了安全的传输,一个TLS/SSL在TCP上 // We issue a read to the SecureTranport API, which in turn issues a read to our SSLReadFunction. //咱们发出read在安全传输的API上,其实就是发出read在SSLReadFunction上 // Our SSLReadFunction then reads from the BSD socket and returns the encrypted data to SecureTransport. //咱们SSLReadFunction 从BSD socket去读,而且返回加密的数据到安全传输中。 // SecureTransport then decrypts the data, and finally returns the decrypted data back to us. // 而后安全传输返回解密的数据,最终把解密的数据返回给咱们 // The first buffer is one we create. //第一个buffe是咱们建立的 // SecureTransport often requests small amounts of data. //安全的传输常常须要少许的数据 // This has to do with the encypted packets that are coming across the TCP stream. //他们不得不用加密包来穿过TCP流 // But it's non-optimal to do a bunch of small reads from the BSD socket. //可是,这是否是最佳的,从BSD Socket上,进行一堆小的阅读 // So our SSLReadFunction reads all available data from the socket (optimizing the sys call) //因此咱们SSLReadFunction从socket中读取全部提供的数据(最佳的方式) // and may store excess in the sslPreBuffer. //可能在sslPreBuffer中存储超出的部分 //预估的读取大小再加上 ssl中可读的 estimatedBytesAvailable += [sslPreBuffer availableBytes]; // The second buffer is within SecureTransport. //第二个Buffer在安全传输中 // As mentioned earlier, there are encrypted packets coming across the TCP stream. //像以前提到的,这里有加密的包在TCP流中 // SecureTransport needs the entire packet to decrypt it. //安全传输须要把整个包解密 // But if the entire packet produces X bytes of decrypted data, //可是若是整个包只有 X字节是加密的数据 // and we only asked SecureTransport for X/2 bytes of data, //而咱们仅仅访问了 SecureTransport中一半字节的数据 // it must store the extra X/2 bytes of decrypted data for the next read. // 咱们必须存储另外一半在下一次读取中 // The SSLGetBufferedReadSize function will tell us the size of this internal buffer. //SSLGetBufferedReadSize方法,将告诉咱们内部的buffer大小 // From the documentation: // // "This function does not block or cause any low-level read operations to occur." //从文档中:这个方法不会阻塞和引发低级别的读取操做发生 size_t sslInternalBufSize = 0; //拿到SSL上下文中的大小,也就是计算咱们能从SSLReead中能获取到的数据大小 SSLGetBufferedReadSize(sslContext, &sslInternalBufSize); //加到预估大小中 estimatedBytesAvailable += sslInternalBufSize; } //若是 estimatedBytesAvailable 大于0 为YES hasBytesAvailable = (estimatedBytesAvailable > 0); } //若是没有数据可读 -- 一次传完的包 if ((hasBytesAvailable == NO) && ([preBuffer availableBytes] == 0)) { LogVerbose(@"No data available to read..."); //并且不是用CFStream if (![self usingCFStreamForTLS]) { // Need to wait for readSource to fire and notify us of // available data in the socket's internal read buffer. //恢复读的source [self resumeReadSource]; } return; } //若是开始 kStartingReadTLS,说明正在准备握手,那么咱们不能进行读取操做,要直接返回 if (flags & kStartingReadTLS) { LogVerbose(@"Waiting for SSL/TLS handshake to complete"); // The readQueue is waiting for SSL/TLS handshake to complete. //若是正在写的TLS,若是上一次是阻塞错误,那么在从新去握手,(防止一次握手阻塞而失败致使再也不握手) if (flags & kStartingWriteTLS) { //若是用的是非CFStreamTLS,即安全的TLS 并且上一次握手错误为 IO阻塞的 if ([self usingSecureTransportForTLS] && lastSSLHandshakeError == errSSLWouldBlock) { // We are in the process of a SSL Handshake. // We were waiting for incoming data which has just arrived. //SSL的握手 [self ssl_continueSSLHandshake]; } } else { // We are still waiting for the writeQueue to drain and start the SSL/TLS process. // We now know data is available to read. //若是当前不是CFStream的方式 if (![self usingCFStreamForTLS]) { // Suspend the read source or else it will continue to fire nonstop. //挂起读的queue [self suspendReadSource]; } } return; } //是否完成读的操做 BOOL done = NO; // Completed read operation //错误 NSError *error = nil; // Error occurred //当前总读的数据量 NSUInteger totalBytesReadForCurrentRead = 0; // // STEP 1 - READ FROM PREBUFFER // //先从提早缓冲区去读,若是缓冲区可读大小大于0 if ([preBuffer availableBytes] > 0) { // There are 3 types of read packets: // // 1) Read all available data. // 2) Read a specific length of data. // 3) Read up to a particular terminator. //3种类型的读法,一、全读、二、读取特定长度、三、读取到一个明确的界限 NSUInteger bytesToCopy; //若是当前读的数据界限不为空 if (currentRead->term != nil) { // Read type #3 - read up to a terminator //直接读到界限 bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; } else { // Read type #1 or #2 //读取数据,读到指定长度或者数据包的长度为止 bytesToCopy = [currentRead readLengthForNonTermWithHint:[preBuffer availableBytes]]; } // Make sure we have enough room in the buffer for our read. //从上两步拿到咱们须要读的长度,去看看有没有空间去存储 [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; // Copy bytes from prebuffer into packet buffer //拿到咱们须要追加数据的指针位置 //当前读的数据 + 开始偏移 + 已经读完的?? uint8_t *buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; //从prebuffer处复制过来数据,bytesToCopy长度 memcpy(buffer, [preBuffer readBuffer], bytesToCopy); // Remove the copied bytes from the preBuffer //从preBuffer移除掉已经复制的数据 [preBuffer didRead:bytesToCopy]; LogVerbose(@"copied(%lu) preBufferLength(%zu)", (unsigned long)bytesToCopy, [preBuffer availableBytes]); // Update totals //已读的数据加上 currentRead->bytesDone += bytesToCopy; //当前已读的数据加上 totalBytesReadForCurrentRead += bytesToCopy; // Check to see if the read operation is done //判断是否是读完了 if (currentRead->readLength > 0) { // Read type #2 - read a specific length of data //若是已读 == 须要读的长度,说明已经读完 done = (currentRead->bytesDone == currentRead->readLength); } //判断界限标记 else if (currentRead->term != nil) { // Read type #3 - read up to a terminator // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method //若是没作完,且读的最大长度大于0,去判断是否溢出 if (!done && currentRead->maxLength > 0) { // We're not done and there's a set maxLength. // Have we reached that maxLength yet? //若是已读的大小大于最大的大小,则报溢出错误 if (currentRead->bytesDone >= currentRead->maxLength) { error = [self readMaxedOutError]; } } } else { // Read type #1 - read all available data // // We're done as soon as // - we've read all available data (in prebuffer and socket) // - we've read the maxLength of read packet. //判断已读大小和最大大小是否相同,相同则读完 done = ((currentRead->maxLength > 0) && (currentRead->bytesDone == currentRead->maxLength)); } } // // STEP 2 - READ FROM SOCKET // 从socket中去读取 //是否读到EOFException ,这个错误指的是文件结尾了还在继续读,就会致使这个错误被抛出 BOOL socketEOF = (flags & kSocketHasReadEOF) ? YES : NO; // Nothing more to read via socket (end of file) //若是没完成,且没错,没读到结尾,且没有可读数据了 BOOL waiting = !done && !error && !socketEOF && !hasBytesAvailable; // Ran out of data, waiting for more //若是没完成,且没错,没读到结尾,有可读数据 if (!done && !error && !socketEOF && hasBytesAvailable) { //断言,有可读数据 NSAssert(([preBuffer availableBytes] == 0), @"Invalid logic"); //是否读到preBuffer中去 BOOL readIntoPreBuffer = NO; uint8_t *buffer = NULL; size_t bytesRead = 0; //若是flag标记为安全socket if (flags & kSocketSecure) { //若是使用CFStream if ([self usingCFStreamForTLS]) { #if TARGET_OS_IPHONE // Using CFStream, rather than SecureTransport, for TLS //默认读的大小32KB NSUInteger defaultReadLength = (1024 * 32); //决定咱们读的字节大小,和是否使用prebuffer NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength shouldPreBuffer:&readIntoPreBuffer]; // Make sure we have enough room in the buffer for our read. // // We are either reading directly into the currentRead->buffer, // or we're reading into the temporary preBuffer. //若是使用preBuffer,则去确保有这么大的空间来存 if (readIntoPreBuffer) { [preBuffer ensureCapacityForWrite:bytesToRead]; //拿到写的buffer buffer = [preBuffer writeBuffer]; } //不用prebuffer else { //确保大小,其实不用。。 [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; //获取到当前buffer上次写到的偏移位置 buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; } // Read data into buffer #pragma mark - 开始读取数据 CFStream //从readStream中读取数据,到buffer中 CFIndex result = CFReadStreamRead(readStream, buffer, (CFIndex)bytesToRead); LogVerbose(@"CFReadStreamRead(): result = %i", (int)result); //读取失败 if (result < 0) { error = (__bridge_transfer NSError *)CFReadStreamCopyError(readStream); } // 读取抛出了EOFException,到数据边界了 else if (result == 0) { socketEOF = YES; } //正常读取 else { waiting = YES; bytesRead = (size_t)result; } // We only know how many decrypted bytes were read. // The actual number of bytes read was likely more due to the overhead of the encryption. // So we reset our flag, and rely on the next callback to alert us of more data. //移除仍然有数据可读的标记 flags &= ~kSecureSocketHasBytesAvailable; #endif } else { //用安全传输来 // Using SecureTransport for TLS // // We know: // - how many bytes are available on the socket // - how many encrypted bytes are sitting in the sslPreBuffer // - how many decypted bytes are sitting in the sslContext // // But we do NOT know: // - how many encypted bytes are sitting in the sslContext // // So we play the regular game of using an upper bound instead. //也是默认32KB NSUInteger defaultReadLength = (1024 * 32); //若是默认大小小于预估的大小,则让默认大小的 = 预估大小 + 16KB ,16KB干吗用的?? if (defaultReadLength < estimatedBytesAvailable) { defaultReadLength = estimatedBytesAvailable + (1024 * 16); } //去要读的大小,还有是否走Prebuffer NSUInteger bytesToRead = [currentRead optimalReadLengthWithDefault:defaultReadLength shouldPreBuffer:&readIntoPreBuffer]; //若是要读的大小大于最大值 ,则让其等于最大值 if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t bytesToRead = SIZE_MAX; } // Make sure we have enough room in the buffer for our read. // // We are either reading directly into the currentRead->buffer, // or we're reading into the temporary preBuffer. //仍是去确保最大空间,而且拿到写的头指针 if (readIntoPreBuffer) { [preBuffer ensureCapacityForWrite:bytesToRead]; buffer = [preBuffer writeBuffer]; } else { [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; } // The documentation from Apple states: // // "a read operation might return errSSLWouldBlock,
// indicating that less data than requested was actually transferred" // // However, starting around 10.7, the function will sometimes return noErr, // even if it didn't read as much data as requested. So we need to watch out for that. OSStatus result; #pragma mark - 开始读取数据 SSLRead //循环去读 do { //拿到当前写到的buffer位置 //头指针 + 读了的大小 void *loop_buffer = buffer + bytesRead; //获得还须要读的大小 size_t loop_bytesToRead = (size_t)bytesToRead - bytesRead; //设置这一次循环读的进度 size_t loop_bytesRead = 0; //用ssl方式去读取数据,头指针为loop_buffer,大小为loop_bytesToRead,进度为loop_bytesRead result = SSLRead(sslContext, loop_buffer, loop_bytesToRead, &loop_bytesRead); LogVerbose(@"read from secure socket = %u", (unsigned)loop_bytesRead); //读了的大小加进度 bytesRead += loop_bytesRead; } //若是没出错,且读的大小小于须要读的大小,就一直循环 while ((result == noErr) && (bytesRead < bytesToRead)); //若是出错 if (result != noErr) { //若是是IO阻塞的错误, waiting if (result == errSSLWouldBlock) waiting = YES; else { //若是是SSL链接断开的错误 if (result == errSSLClosedGraceful || result == errSSLClosedAbort) { // We've reached the end of the stream. // Handle this the same way we would an EOF from the socket. //说明到边界了 socketEOF = YES; //把错误赋值给SSLErrCode sslErrCode = result; } else { //直接拿到SSL数据错误 error = [self sslError:result]; } } // It's possible that bytesRead > 0, even if the result was errSSLWouldBlock. //颇有可能bytesRead中有数据,即便结果是IO阻塞的错误 // This happens when the SSLRead function is able to read some data, // but not the entire amount we requested. if (bytesRead <= 0) { bytesRead = 0; } } //不要修改 socketFDBytesAvailable 可读数据大小,由于这个会在 SSLReadFunction中被修改 // Do not modify socketFDBytesAvailable. // It will be updated via the SSLReadFunction(). } } else { // Normal socket operation //普通的socket 操做 NSUInteger bytesToRead; // There are 3 types of read packets: // // 1) Read all available data. // 2) Read a specific length of data. // 3) Read up to a particular terminator. //和上面相似,读取到边界标记??不是吧 if (currentRead->term != nil) { // Read type #3 - read up to a terminator //读这个长度,若是到maxlength,就用maxlength。看若是可用空间大于须要读的空间,则不用prebuffer bytesToRead = [currentRead readLengthForTermWithHint:estimatedBytesAvailable shouldPreBuffer:&readIntoPreBuffer]; } else { // Read type #1 or #2 //直接读这个长度,若是到maxlength,就用maxlength bytesToRead = [currentRead readLengthForNonTermWithHint:estimatedBytesAvailable]; } //大于最大值,则先读最大值 if (bytesToRead > SIZE_MAX) { // NSUInteger may be bigger than size_t (read param 3) bytesToRead = SIZE_MAX; } // Make sure we have enough room in the buffer for our read. // // We are either reading directly into the currentRead->buffer, // or we're reading into the temporary preBuffer. if (readIntoPreBuffer) { [preBuffer ensureCapacityForWrite:bytesToRead]; buffer = [preBuffer writeBuffer]; } else { [currentRead ensureCapacityForAdditionalDataOfLength:bytesToRead]; buffer = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; } // Read data into buffer int socketFD = (socket4FD != SOCKET_NULL) ? socket4FD : (socket6FD != SOCKET_NULL) ? socket6FD : socketUN; #pragma mark - 开始读取数据,最普通的形式 read //读数据 ssize_t result = read(socketFD, buffer, (size_t)bytesToRead); LogVerbose(@"read from socket = %i", (int)result); //读取错误 if (result < 0) { //EWOULDBLOCK IO阻塞 if (errno == EWOULDBLOCK) //先等待 waiting = YES; else //获得错误 error = [self errnoErrorWithReason:@"Error in read() function"]; //把可读取的长度设置为0 socketFDBytesAvailable = 0; } //读到边界了 else if (result == 0) { socketEOF = YES; socketFDBytesAvailable = 0; } //正常 else { //设置读到的数据长度 bytesRead = result; //若是读到的数据小于应该读的长度,说明这个包没读完 if (bytesRead < bytesToRead) { // The read returned less data than requested. // This means socketFDBytesAvailable was a bit off due to timing, // because we read from the socket right when the readSource event was firing. socketFDBytesAvailable = 0; } //正常 else { //若是 socketFDBytesAvailable比读了的数据小的话,直接置为0 if (socketFDBytesAvailable <= bytesRead) socketFDBytesAvailable = 0; //减去已读大小 else socketFDBytesAvailable -= bytesRead; } //若是 socketFDBytesAvailable 可读数量为0,把读的状态切换为等待 if (socketFDBytesAvailable == 0) { waiting = YES; } } } //若是此次读的字节大于0 if (bytesRead > 0) { // Check to see if the read operation is done //检查这个包的数据是否读完,用readLength来读的 if (currentRead->readLength > 0) { // Read type #2 - read a specific length of data // // Note: We should never be using a prebuffer when we're reading a specific length of data. //咱们读取固定大小的时候是永远不用写到prebuffer中去的 //断言,是不须要写到prebuffer中去的 NSAssert(readIntoPreBuffer == NO, @"Invalid logic"); //加上读的数量 currentRead->bytesDone += bytesRead; //把这一次读的数量加上来 totalBytesReadForCurrentRead += bytesRead; //判断是否已读完 done = (currentRead->bytesDone == currentRead->readLength); } //用边界来读的 else if (currentRead->term != nil) { // Read type #3 - read up to a terminator //若是是往buffer中读的 if (readIntoPreBuffer) { // We just read a big chunk of data into the preBuffer //移动writeBuffer的指针 [preBuffer didWrite:bytesRead]; LogVerbose(@"read data into preBuffer - preBuffer.length = %zu", [preBuffer availableBytes]); // Search for the terminating sequence //拿到须要读取的大小,根据term,而且判断是否已读完 NSUInteger bytesToCopy = [currentRead readLengthForTermWithPreBuffer:preBuffer found:&done]; LogVerbose(@"copying %lu bytes from preBuffer", (unsigned long)bytesToCopy); // Ensure there's room on the read packet's buffer //确保有这么大的空间 [currentRead ensureCapacityForAdditionalDataOfLength:bytesToCopy]; // Copy bytes from prebuffer into read buffer uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; #pragma mark - 把数据从preBuffer中移到currentRead上 memcpy(readBuf, [preBuffer readBuffer], bytesToCopy); // Remove the copied bytes from the prebuffer //标记已经读了这么多数据 [preBuffer didRead:bytesToCopy]; LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); // Update totals currentRead->bytesDone += bytesToCopy; totalBytesReadForCurrentRead += bytesToCopy; // Our 'done' variable was updated via the readLengthForTermWithPreBuffer:found: method above } //没有用prebuffer else { // We just read a big chunk of data directly into the packet's buffer. // We need to move any overflow into the prebuffer. //咱们须要把数据流向prebuffer? //拿到粘包长度,(为溢出长度,溢出的咱们要写到prebuffer中去。给下一个包去读) NSInteger overflow = [currentRead searchForTermAfterPreBuffering:bytesRead]; //若是为0,说明彻底匹配 if (overflow == 0) { //加上此次读取的字节数 currentRead->bytesDone += bytesRead; //总的读取字节数 totalBytesReadForCurrentRead += bytesRead; //标志读取完成 done = YES; } //说明读取的数据总长度比当前包大(粘包) else if (overflow > 0) { //当前包内的长度 NSInteger underflow = bytesRead - overflow; // Copy excess data into preBuffer LogVerbose(@"copying %ld overflow bytes into preBuffer", (long)overflow); //确保preBuffer有这么大的大小 [preBuffer ensureCapacityForWrite:overflow]; //把buffer日后移,去掉重合的数据大小 uint8_t *overflowBuffer = buffer + underflow; //写到writeBuffer中,长度为 overflow(非重合部分) memcpy([preBuffer writeBuffer], overflowBuffer, overflow); //后移写指针 [preBuffer didWrite:overflow]; LogVerbose(@"preBuffer.length = %zu", [preBuffer availableBytes]); // Note: The completeCurrentRead method will trim the buffer for us. //加上已读的大小(非粘包的) currentRead->bytesDone += underflow; //此次总共读取的大小 totalBytesReadForCurrentRead += underflow; //当前读取完成 done = YES; } //数据还没达到边界 else { // The term was not found within the data that we read. //已读的加上 bytesRead currentRead->bytesDone += bytesRead; totalBytesReadForCurrentRead += bytesRead; //标记为未完成 done = NO; } } //若是未完成 并且当前包的数据包最大长度大于0 if (!done && currentRead->maxLength > 0) { // We're not done and there's a set maxLength. // Have we reached that maxLength yet? //判断写的大小 是否达到包的最大值 if (currentRead->bytesDone >= currentRead->maxLength) { //获得读取溢出的错误 error = [self readMaxedOutError]; } } } //没边界,没给定长度(没法判断当前包结尾) else { // Read type #1 - read all available data //若是从prebuffer中读取 if (readIntoPreBuffer) { // We just read a chunk of data into the preBuffer //指针后移 [preBuffer didWrite:bytesRead]; // Now copy the data into the read packet. // // Recall that we didn't read directly into the packet's buffer to avoid // over-allocating memory since we had no clue how much data was available to be read. // // Ensure there's room on the read packet's buffer //确保currentRead中有bytesRead大小可用 [currentRead ensureCapacityForAdditionalDataOfLength:bytesRead]; // Copy bytes from prebuffer into read buffer uint8_t *readBuf = (uint8_t *)[currentRead->buffer mutableBytes] + currentRead->startOffset + currentRead->bytesDone; //拿到指针赋值 memcpy(readBuf, [preBuffer readBuffer], bytesRead); // Remove the copied bytes from the prebuffer //标记读了这么多数据 [preBuffer didRead:bytesRead]; // Update totals //更新已读 currentRead->bytesDone += bytesRead; totalBytesReadForCurrentRead += bytesRead; } //在currentRead中的话直接加就行 else { currentRead->bytesDone += bytesRead; totalBytesReadForCurrentRead += bytesRead; } //由于没法判断结尾,因此每次读都会直接标记为YES,即一个包完成 done = YES; } } // if (bytesRead > 0) } // if (!done && !error && !socketEOF && hasBytesAvailable) //若是未完成,并且没有应读长度和边界符 if (!done && currentRead->readLength == 0 && currentRead->term == nil) { // Read type #1 - read all available data // // We might arrive here if we read data from the prebuffer but not from the socket. //只要当前总共读的数量大于0,就认为完成了,由于无从判断 done = (totalBytesReadForCurrentRead > 0); } // Check to see if we're done, or if we've made progress //检查是否读完 if (done) { //完成此次数据的读取 [self completeCurrentRead]; //若是没出错,没有到边界,prebuffer中还有可读数据 if (!error && (!socketEOF || [preBuffer availableBytes] > 0)) { //让读操做离队,继续进行下一次读取 [self maybeDequeueRead]; } } //若是此次读的数量大于0 else if (totalBytesReadForCurrentRead > 0) { // We're not done read type #2 or #3 yet, but we have read in some bytes __strong id theDelegate = delegate; //若是响应读数据进度的代理 if (delegateQueue && [theDelegate respondsToSelector:@selector(socket:didReadPartialDataOfLength:tag:)]) { long theReadTag = currentRead->tag; //代理queue中回调出去 dispatch_async(delegateQueue, ^{ @autoreleasepool { [theDelegate socket:self didReadPartialDataOfLength:totalBytesReadForCurrentRead tag:theReadTag]; }}); } } // Check for errors //检查错误 if (error) { //若是有错直接报错断开链接 [self closeWithError:error]; } //若是是读到边界错误 else if (socketEOF) { [self doReadEOF]; } //若是是等待 else if (waiting) { //若是用的是CFStream,则读取数据和source无关 //非CFStream形式 if (![self usingCFStreamForTLS]) { // Monitor the socket for readability (if we're not already doing so) //从新恢复source [self resumeReadSource]; } } // Do not add any code here without first adding return statements in the error cases above. } 复制代码
特别特别长...接下来就是关闭socket了socket
- (void)closeWithError:(NSError *)error
{
LogTrace();
//先判断当前queue是否是IsOnSocketQueueOrTargetQueueKey
NSAssert(dispatch_get_specific(IsOnSocketQueueOrTargetQueueKey), @"Must be dispatched on socketQueue");
//关闭链接超时
[self endConnectTimeout];
if (currentRead != nil) [self endCurrentRead];
if (currentWrite != nil) [self endCurrentWrite];
[readQueue removeAllObjects];
[writeQueue removeAllObjects];
[preBuffer reset];
#if TARGET_OS_IPHONE
{
if (readStream || writeStream)
{
[self removeStreamsFromRunLoop];
if (readStream)
{
CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
CFReadStreamClose(readStream);
CFRelease(readStream);
readStream = NULL;
}
if (writeStream)
{
CFWriteStreamSetClient(writeStream, kCFStreamEventNone, NULL, NULL);
CFWriteStreamClose(writeStream);
CFRelease(writeStream);
writeStream = NULL;
}
}
}
#endif
[sslPreBuffer reset];
sslErrCode = lastSSLHandshakeError = noErr;
if (sslContext)
{
// Getting a linker error here about the SSLx() functions?
// You need to add the Security Framework to your application.
//关闭sslContext
SSLClose(sslContext);
#if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
CFRelease(sslContext);
#else
SSLDisposeContext(sslContext);
#endif
sslContext = NULL;
}
// For some crazy reason (in my opinion), cancelling a dispatch source doesn't // invoke the cancel handler if the dispatch source is paused. // So we have to unpause the source if needed. // This allows the cancel handler to be run, which in turn releases the source and closes the socket. //若是这些source都为空,直接只关闭socket就能够 if (!accept4Source && !accept6Source && !acceptUNSource && !readSource && !writeSource) { LogVerbose(@"manually closing close"); if (socket4FD != SOCKET_NULL) { LogVerbose(@"close(socket4FD)"); close(socket4FD); socket4FD = SOCKET_NULL; } if (socket6FD != SOCKET_NULL) { LogVerbose(@"close(socket6FD)"); close(socket6FD); socket6FD = SOCKET_NULL; } if (socketUN != SOCKET_NULL) { LogVerbose(@"close(socketUN)"); close(socketUN); socketUN = SOCKET_NULL; //断开Unix domin socket unlink(socketUrl.path.fileSystemRepresentation); socketUrl = nil; } } else { //都去取消souce先 if (accept4Source) { LogVerbose(@"dispatch_source_cancel(accept4Source)"); dispatch_source_cancel(accept4Source); // We never suspend accept4Source accept4Source = NULL; } if (accept6Source) { LogVerbose(@"dispatch_source_cancel(accept6Source)"); dispatch_source_cancel(accept6Source); // We never suspend accept6Source accept6Source = NULL; } if (acceptUNSource) { LogVerbose(@"dispatch_source_cancel(acceptUNSource)"); dispatch_source_cancel(acceptUNSource); // We never suspend acceptUNSource acceptUNSource = NULL; } //读写source须要resume,不然若是是suspend状态的话,cancel不会被调用 if (readSource) { LogVerbose(@"dispatch_source_cancel(readSource)"); dispatch_source_cancel(readSource); [self resumeReadSource]; readSource = NULL; } if (writeSource) { LogVerbose(@"dispatch_source_cancel(writeSource)"); dispatch_source_cancel(writeSource); [self resumeWriteSource]; writeSource = NULL; } // The sockets will be closed by the cancel handlers of the corresponding source socket4FD = SOCKET_NULL; socket6FD = SOCKET_NULL; socketUN = SOCKET_NULL; } // If the client has passed the connect/accept method, then the connection has at least begun. // Notify delegate that it is now ending. //判断是否sokcet开启 BOOL shouldCallDelegate = (flags & kSocketStarted) ? YES : NO; BOOL isDeallocating = (flags & kDealloc) ? YES : NO; // Clear stored socket info and all flags (config remains as is) //清楚socket的相关信息,和全部标记 socketFDBytesAvailable = 0; flags = 0; sslWriteCachedLength = 0; if (shouldCallDelegate) { __strong id theDelegate = delegate; //判断是否须要传本身过去,若是已经被销毁,就传nil __strong id theSelf = isDeallocating ? nil : self; //调用断开链接的代理 if (delegateQueue && [theDelegate respondsToSelector: @selector(socketDidDisconnect:withError:)]) { dispatch_async(delegateQueue, ^{ @autoreleasepool { [theDelegate socketDidDisconnect:theSelf withError:error]; }}); } } } 复制代码