GCDAsyncSocket源码分析

类分析

  • GCDAsyncSocket

  • 状态的枚举

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;//当前类的状态
    uint16_t config;
    
    uint16_t max4ReceiveSize;//最大接收数据包的大小
    uint32_t max6ReceiveSize;
    
    int socket4FD;//socket文件描述符
    int socket6FD;
    
    dispatch_queue_t socketQueue;//socket队列
    
    dispatch_source_t send4Source;//发送的source
    dispatch_source_t send6Source;
    dispatch_source_t receive4Source;
    dispatch_source_t receive6Source;
    dispatch_source_t sendTimer;//发送的定时器
    
    GCDAsyncUdpSendPacket *currentSend;//当前发送的数据包
    NSMutableArray *sendQueue;//发送队列
    
    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的标识符    
    
#if TARGET_OS_IPHONE
    CFStreamClientContext streamContext;
    CFReadStreamRef readStream4;
    CFReadStreamRef readStream6;
    CFWriteStreamRef writeStream4;
    CFWriteStreamRef writeStream6;
#endif
    
    id userData;
  • GCDAsyncUdpSendPacket
    NSData *buffer;//发送的数据
    NSTimeInterval timeout;//超时的时间
    long tag;//发送数据包的tag
    
    BOOL resolveInProgress;//解决中
    BOOL filterInProgress;//过滤中
    
    NSArray *resolvedAddresses;//被解决的地址
    NSError *resolveError;//解决出现的错误
    
    NSData *address;//地址
    int addressFamily;

发送数据

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

这个方法中实例化了发送包,并把host和port转化为地址(参见getaddrinfo()函数)

GCDAsyncSocket发送消息方法详解

<>中的是方法名,有些方法名没有写全,所以配合代码食用更佳。


<SendData>
创建GCDAsyncUdpSendPacket
host转address
做检查操作   <doPreSend>
把packet装入sendQueue中
执行出列操作  <maybeDequeueSend>

<maybeDequeueSend>
创建socket 执行 <createSockets>
出列当前sendQueue中的packet并作为currentSend
特殊状态处理GCDAsyncUdpSpecialPacket
错误处理
做预处理,检查包    执行 <doPreSend>

<doPreSend>
有连接
如果currentSend正在进行其他操作,返回错误
如果没有,给currentSend赋值
无连接
packet进行其他操作,等待
遇上错误,返回
没地址的处理
    两种连接最后总要给currentSend的address和addressFamily属性赋值。
等待的处理:暂停 source
错误处理
Filter处理
没有Filter情况直接执行      <doSend>

<doSend>
有连接
发送方法为result=send()
无连接
发送方法为result=sendto() 参数中包含地址参数
根据result来判断是否等待
错误处理是等待还是返回错误
等待情况的处理(没有足够空间)
当当前没有不能接收数据的时候(kSock4CanAcceptBytes)说明有空间了,所以要重新开始source
    设置超时相应
        超时执行    <maybeDequeueSend>
错误处理
正常情况
    继续从sendQueue中拿出packet来继续搞事 执行   <maybeDequeueSend>

<createSockets>
判断IPv4|6是否可用
创建socket 执行         <createSocket4>

<createSocket4>
创建socket的文件描述符
设置发送和接收source       执行  <setupSendAndReceiveSourcesForSocket4>

<setupSendAndReceiveSourcesForSocket4>
创建source
设置event
    send source
    出错处理
    执行  <suspendSend4Source>
    无错处理
    执行  <doSend>

    receive source
    出错处理
    无错处理
    执行  <doReceive>
设置cancel

<doReceive>
通过flag判断是否该暂停或者重启receive source
计算是否该用IPv6|4;分有连接和无连接的情况
执行socket IO;使用result=recvfrom()函数接收数据 包括data
根据result判断是否等待,错误处理,然后执行代理方法通知接收到了数据。filter的操作。
对等待和错误处理的值进行处理。
无错处理
flag&kReceiveContinuous 继续接收
接收一次    执行      <doReceive>

例子:
一个packet过来
先进行地址转换resolveInProgress = yes
进行转换的时候,同时进行出列操作,然后执行doPreSend
doPreSend中resolveInProgress会导致source暂停
然后当地址转换进行完毕的时候,会再次调用doPreSend方法
这时候就会直接去调用doSend方法
调用sendto()方法

doPreSend的时候会因为currentSend正在干别的事情而暂停source

这个过程没有讲filter的相关内容。

预计理解dispatch_source/time的相关内容。
c的函数库的那些方法和结构体作为了解内容。
还有代码组织方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容