GCDAsyncSocket源碼分析

類分析

  • GCDAsyncSocket

  • 狀態(tài)的枚舉

enum GCDAsyncUdpSocketFlags
{
    kDidCreateSockets        = 1 <<  0,  // If set, the sockets have been created.
    kDidBind                 = 1 <<  1,  // If set, bind has been called.
    kConnecting              = 1 <<  2,  // If set, a connection attempt is in progress.
    kDidConnect              = 1 <<  3,  // If set, socket is connected.
    kReceiveOnce             = 1 <<  4,  // If set, one-at-a-time receive is enabled
    kReceiveContinuous       = 1 <<  5,  // If set, continuous receive is enabled
    kIPv4Deactivated         = 1 <<  6,  // If set, socket4 was closed due to bind or connect on IPv6.
    kIPv6Deactivated         = 1 <<  7,  // If set, socket6 was closed due to bind or connect on IPv4.
    kSend4SourceSuspended    = 1 <<  8,  // If set, send4Source is suspended.
    kSend6SourceSuspended    = 1 <<  9,  // If set, send6Source is suspended.
    kReceive4SourceSuspended = 1 << 10,  // If set, receive4Source is suspended.
    kReceive6SourceSuspended = 1 << 11,  // If set, receive6Source is suspended.
    kSock4CanAcceptBytes     = 1 << 12,  // If set, we know socket4 can accept bytes. If unset, it's unknown.
    kSock6CanAcceptBytes     = 1 << 13,  // If set, we know socket6 can accept bytes. If unset, it's unknown.
    kForbidSendReceive       = 1 << 14,  // If set, no new send or receive operations are allowed to be queued.
    kCloseAfterSends         = 1 << 15,  // If set, close as soon as no more sends are queued.
    kFlipFlop                = 1 << 16,  // Used to alternate between IPv4 and IPv6 sockets.
#if TARGET_OS_IPHONE
    kAddedStreamListener     = 1 << 17,  // If set, CFStreams have been added to listener thread
#endif
};
  • Param
#if __has_feature(objc_arc_weak)
    __weak id delegate;
#else
    __unsafe_unretained id delegate;
#endif
    dispatch_queue_t delegateQueue;
    
    GCDAsyncUdpSocketReceiveFilterBlock receiveFilterBlock;
    dispatch_queue_t receiveFilterQueue;
    BOOL receiveFilterAsync;
    
    GCDAsyncUdpSocketSendFilterBlock sendFilterBlock;
    dispatch_queue_t sendFilterQueue;
    BOOL sendFilterAsync;
    
    uint32_t flags;//當(dāng)前類的狀態(tài)
    uint16_t config;
    
    uint16_t max4ReceiveSize;//最大接收數(shù)據(jù)包的大小
    uint32_t max6ReceiveSize;
    
    int socket4FD;//socket文件描述符
    int socket6FD;
    
    dispatch_queue_t socketQueue;//socket隊列
    
    dispatch_source_t send4Source;//發(fā)送的source
    dispatch_source_t send6Source;
    dispatch_source_t receive4Source;
    dispatch_source_t receive6Source;
    dispatch_source_t sendTimer;//發(fā)送的定時器
    
    GCDAsyncUdpSendPacket *currentSend;//當(dāng)前發(fā)送的數(shù)據(jù)包
    NSMutableArray *sendQueue;//發(fā)送隊列
    
    unsigned long socket4FDBytesAvailable;//
    unsigned long socket6FDBytesAvailable;
    
    uint32_t pendingFilterOperations;
    
    NSData   *cachedLocalAddress4;
    NSString *cachedLocalHost4;
    uint16_t  cachedLocalPort4;
    
    NSData   *cachedLocalAddress6;
    NSString *cachedLocalHost6;
    uint16_t  cachedLocalPort6;
    
    NSData   *cachedConnectedAddress;
    NSString *cachedConnectedHost;
    uint16_t  cachedConnectedPort;
    int       cachedConnectedFamily;

    void *IsOnSocketQueueOrTargetQueueKey;//socketQueue的標(biāo)識符    
    
#if TARGET_OS_IPHONE
    CFStreamClientContext streamContext;
    CFReadStreamRef readStream4;
    CFReadStreamRef readStream6;
    CFWriteStreamRef writeStream4;
    CFWriteStreamRef writeStream6;
#endif
    
    id userData;
  • GCDAsyncUdpSendPacket
    NSData *buffer;//發(fā)送的數(shù)據(jù)
    NSTimeInterval timeout;//超時的時間
    long tag;//發(fā)送數(shù)據(jù)包的tag
    
    BOOL resolveInProgress;//解決中
    BOOL filterInProgress;//過濾中
    
    NSArray *resolvedAddresses;//被解決的地址
    NSError *resolveError;//解決出現(xiàn)的錯誤
    
    NSData *address;//地址
    int addressFamily;

發(fā)送數(shù)據(jù)

  - (void)sendData:(NSData *)data toHost:(NSString *)host port:(uint16_t)port withTimeout:(NSTimeInterval)timeout tag:(long)tag;

這個方法中實例化了發(fā)送包,并把host和port轉(zhuǎn)化為地址(參見getaddrinfo()函數(shù))

GCDAsyncSocket發(fā)送消息方法詳解

<>中的是方法名,有些方法名沒有寫全,所以配合代碼食用更佳。


<SendData>
創(chuàng)建GCDAsyncUdpSendPacket
host轉(zhuǎn)address
做檢查操作   <doPreSend>
把packet裝入sendQueue中
執(zhí)行出列操作  <maybeDequeueSend>

<maybeDequeueSend>
創(chuàng)建socket 執(zhí)行 <createSockets>
出列當(dāng)前sendQueue中的packet并作為currentSend
特殊狀態(tài)處理GCDAsyncUdpSpecialPacket
錯誤處理
做預(yù)處理,檢查包    執(zhí)行 <doPreSend>

<doPreSend>
有連接
如果currentSend正在進(jìn)行其他操作,返回錯誤
如果沒有,給currentSend賦值
無連接
packet進(jìn)行其他操作,等待
遇上錯誤,返回
沒地址的處理
    兩種連接最后總要給currentSend的address和addressFamily屬性賦值。
等待的處理:暫停 source
錯誤處理
Filter處理
沒有Filter情況直接執(zhí)行      <doSend>

<doSend>
有連接
發(fā)送方法為result=send()
無連接
發(fā)送方法為result=sendto() 參數(shù)中包含地址參數(shù)
根據(jù)result來判斷是否等待
錯誤處理是等待還是返回錯誤
等待情況的處理(沒有足夠空間)
當(dāng)當(dāng)前沒有不能接收數(shù)據(jù)的時候(kSock4CanAcceptBytes)說明有空間了,所以要重新開始source
    設(shè)置超時相應(yīng)
        超時執(zhí)行    <maybeDequeueSend>
錯誤處理
正常情況
    繼續(xù)從sendQueue中拿出packet來繼續(xù)搞事 執(zhí)行   <maybeDequeueSend>

<createSockets>
判斷IPv4|6是否可用
創(chuàng)建socket 執(zhí)行         <createSocket4>

<createSocket4>
創(chuàng)建socket的文件描述符
設(shè)置發(fā)送和接收source       執(zhí)行  <setupSendAndReceiveSourcesForSocket4>

<setupSendAndReceiveSourcesForSocket4>
創(chuàng)建source
設(shè)置event
    send source
    出錯處理
    執(zhí)行  <suspendSend4Source>
    無錯處理
    執(zhí)行  <doSend>

    receive source
    出錯處理
    無錯處理
    執(zhí)行  <doReceive>
設(shè)置cancel

<doReceive>
通過flag判斷是否該暫停或者重啟receive source
計算是否該用IPv6|4;分有連接和無連接的情況
執(zhí)行socket IO;使用result=recvfrom()函數(shù)接收數(shù)據(jù) 包括data
根據(jù)result判斷是否等待,錯誤處理,然后執(zhí)行代理方法通知接收到了數(shù)據(jù)。filter的操作。
對等待和錯誤處理的值進(jìn)行處理。
無錯處理
flag&kReceiveContinuous 繼續(xù)接收
接收一次    執(zhí)行      <doReceive>

例子:
一個packet過來
先進(jìn)行地址轉(zhuǎn)換resolveInProgress = yes
進(jìn)行轉(zhuǎn)換的時候,同時進(jìn)行出列操作,然后執(zhí)行doPreSend
doPreSend中resolveInProgress會導(dǎo)致source暫停
然后當(dāng)?shù)刂忿D(zhuǎn)換進(jìn)行完畢的時候,會再次調(diào)用doPreSend方法
這時候就會直接去調(diào)用doSend方法
調(diào)用sendto()方法

doPreSend的時候會因為currentSend正在干別的事情而暫停source

這個過程沒有講filter的相關(guān)內(nèi)容。

預(yù)計理解dispatch_source/time的相關(guān)內(nèi)容。
c的函數(shù)庫的那些方法和結(jié)構(gòu)體作為了解內(nèi)容。
還有代碼組織方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 4,051評論 0 7
  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理。運(yùn)行時機(jī)制的原理和運(yùn)用場景。SDWebImage的原...
    LZM輪回閱讀 2,124評論 0 12
  • 史上最全的iOS面試題及答案 iOS面試小貼士———————————————回答好下面的足夠了----------...
    Style_偉閱讀 2,575評論 0 35
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評論 19 139
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛DE問候閱讀 1,843評論 0 4

友情鏈接更多精彩內(nèi)容