由于上一篇文章篇幅过长移到这边。
3.read&write
- 先看write
//写数据对外方法
- (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安全等等,需要有设置才会执行
- (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,看看会发现,方法格式差不多
- read
- (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包,然后添加到数组中,然后执行下一个方法
- (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还长,我觉得有必要列一下这个方法里面都做了什么。。。
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种类型的读法,1、全读、2、读取特定长度、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了
5.CLOSE
- (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];
}});
}
}
}