AsynSocket 源码解析之二

CocoaAsyncSocket 源码学习摘要:

GCDAsynSocket 读取socket数据(接收对方发送过来的数据)调用:read(socketFD, buffer, (size_t)bytesToRead)。写入socket数据(向对方发送数据)调用 write(socketFD, buffer, (size_t)bytesToWrite)。一般调用read/write 方法的时候需要用result变量存储返回结果如:ssize_t result = read(socketFD, buffer, (size_t)bytesToRead);想知道result都有可能是什么值?
我们先简单看下read函数(write函数基本类似)说明:size_t read(int fd,void *buf,size_t nbyte)。read函数是负责从fd中读取内容。

  • 返回值 > 0,表示读取成功。read返回实际读取到的字节数。
  • 返回值 = 0,表示已经读取到文件的结束了(此时应该关闭socket或者仅关闭读流操作)。
  • 返回值 < 0,表示是读取错误。如果错误是EINTR表示在写的时候出现了中断错误;如果是EPIPE表示网络连接出现了问题;如果是EWOULDBLOCK or EAGAIN(non-blocking IO的时候才会发生),继续尝试读取。

想了解更多相关错误码,请参考:
[EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接]
[IT牛人博客聚合]

所以在GCDAsynSocket源代码中我们经常看到下面代码或者类似代码(我加入了注释):


int socketFD = (socket4FD == SOCKET_NULL) ? socket6FD : socket4FD;//ipv4 or ipv6
//希望读取bytesToRead字节长度的数据到buffer缓存中。 
ssize_t result = read(socketFD, buffer, (size_t)bytesToRead);
LogVerbose(@"read from socket = %i", (int)result);

if (result < 0)//表示失败
{
    if (errno == EWOULDBLOCK)//异步IO需要处理(GCDAsynSocket用的是异步IO)
        //没有读到数据,下次可以继续读。设置waiting = YES
        //这种情况下后面可能就会调用resumeReadSource,继续监听socket。
        //当有数据可读的时候,继续尝试读取,具体原因后面会简单介绍。
        waiting = YES;
    else
        //后面会执行断开连接逻辑
        error = [self errnoErrorWithReason:@"Error in read() function"];
                
        socketFDBytesAvailable = 0;
}else if (result == 0)
{
    //读到socket末尾。遇到这种情况,后面会根据GCDSocket的kAllowHalfDuplexConnection设置做对应处理
    //如果kAllowHalfDuplexConnection = NO,后面会关闭socket
    //如果kAllowHalfDuplexConnection = Yes,后面会关闭读流操作(写流还可以进行),但不一定关闭socket。
    socketEOF = YES;
    socketFDBytesAvailable = 0;
}
else//表示soket数据读取成功
{
    bytesRead = result;//实际从socket获取到的字节数据
                
    if (socketFDBytesAvailable <= bytesRead)
        socketFDBytesAvailable = 0;//提供的数据都被读完了。
    else
        socketFDBytesAvailable -= bytesRead;
                
    if (socketFDBytesAvailable == 0)
    {
        //都是为了在一些复杂的情况下,防止sokect因为调用了suspendReadSource,导致不能继续工作。
        waiting = YES;  
    }
}

下面简单解释下result 返回不同值的时候GCDAsynSocket会做如何处理

  • result > 0,表示从socket读取了result字节数据。socketFDBytesAvailable用于实时记录socket可读数据字节数。socketFDBytesAvailable <= bytesRead表示socket提供的所有数据都已经被读取。当socket所有可提供数据被读取完之后,需要将waiting设置为Yes。一旦调用 waiting == YES,后面会调用resumeReadSource(代码没有贴出来)来继续监听socket提供可读数据字节数。防止因为在一些复杂的情况下,sokect因为调用了suspendReadSource,不能继续监听socket,当socket有数据可读的时候不会回调。
  • result < 0,需要检查返回错误码。由于GCDAsynSocket采用的是异步IO(non-blocking IO)需要处理错误码为EWOULDBLOCK的情况。错误码为EWOULDBLOCK表示这次没有读到数据,后面会继续尝试调用read函数(waiting设置为Yes和上面解释一样)。如果是其他非EWOULDBLOCK错误码需要关闭socket。
  • result = 0,表示读到socket末尾,遇到这种情况,后面会根据GCDSocket的kAllowHalfDuplexConnection设置做对应处理。如果kAllowHalfDuplexConnection = NO,后面会关闭socket;否则后面会关闭socket读流操作,然后检查socket的写流是否被关闭,如果写流也被关闭,那么就会关闭socket(读流写流操作都已经完成)。

下面简单解释下if (errno == EWOULDBLOCK)这句。
errno是系统库API。只有当一个库函数失败时,errno才会被设置。用来记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义。errno详细解释

extern int * __error(void);
#define errno (*__error())

EWOULDBLOCK是一个什么鬼?

系统API 解释:
/* non-blocking and interrupt i/o */
#define EAGAIN      35      /* Resource temporarily unavailable */
#define EWOULDBLOCK EAGAIN      /* Operation would block */

什么情况下回返回 EWOULDBLOCK?

在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返 回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。

参考:
[EAGAIN、EWOULDBLOCK、EINTR与非阻塞 长连接]
[IT牛人博客聚合]

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

推荐阅读更多精彩内容