iOS GCDAsyncSocket源碼分析(二)

因爲上一篇文章篇幅過長移到這邊。數組

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安全等等,須要有設置纔會執行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

  • 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包,而後添加到數組中,而後執行下一個方法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

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]; }}); } } } 複製代碼
相關文章
相關標籤/搜索