Csocket非阻塞模式
一、前言
初期学习socket的时候,为了方便理解,使用默认的阻塞模式比较多。而实际做项目时,我们必须考虑程序的并发性,非阻塞模式在其中担任着很重要的角色,是必会的点之一。本文不对阻塞IO和非阻塞IO的概念做说明,不了解的请自行了解。下文代码以linux平台为例。
二、设置非阻塞模式
设置非阻塞模式,通过fcntl方法设置,为了保存socket其他设置,一般选择先获取 status flags, 并在其基础上设置O_NONBLOCK属性, 代码如下:int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);
fcntl失败返回值为-1, 同时errno会被设置成对应的错误码。(errno在此不做说明,不了解的自行了解。) 考虑失败的情况,个人注意到网上有些例子(包括ss-libev项目)在 F_GETFL 失败后,给了flags默认值,代码如下:int flags; if (-1 == (flags = fcntl(fd, F_GETFL, 0))) { flags = 0; } fcntl(fd, F_SETFL, flags | O_NONBLOCK);
经过测试,默认情况下,flags得到的值为2,也就是O_RDWR 读写, 而 0 对应的相关宏为O_RDONLY只读,明显不合理。个人感觉,对于一个正常的socket来说,F_GETFL 出错的机会不大吧, 至少我是没遇到过。如果实在出错了,还是建议走错误流程而不是给个默认值。
三、 非阻塞server
server端通常在accept后,我们为客户端连接的fd设置为非阻塞。设置O_NONBLOCK后,recv和send发生了变化。默认阻塞模式下,recv在没有数据可以接收(对方未发数据,或者缓冲区的数据已读完对方没有继续发)情况下,recv会阻塞等待,直到下次有数据发送过来。而非阻塞模式下,recv在没有数据可以接收的时候, recv会直接返回-1, 同时errno会被设置为EAGAIN/EWOULDBLOCK 。同理,非阻塞send也会在对方缓冲区满的情况下直接返回-1并设置errno, 而不是阻塞等待。 非阻塞模式下server代码大致如下:int cli_fd = accept(listen_fd, NULL, NULL); //... //省略出错判断和设置非阻塞 //... while (true) { int n = recv(cli_fd, buf, buf_len, 0); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { //无数据,做其他业务逻辑或继续下一轮逻辑, 这里sleep一秒并接着等数据 sleep(1); continue; } else { //错误,可利用errno判断出错原因,这里直接结束 close(cli_fd); break; } } else if (n == 0) { //对方关闭 close(cli_fd); break; } //正常读到数据,处理buf }
四、非阻塞client
client除了在send/recv, 还可以在connect前设置非阻塞模式,这样在connect时候可以直接返回。
client 非阻塞connect的时候,如果返回0表示连接成功,如果返回-1, 则需要判断errno 是否为EINPROGRESS,EINPROGRESS表示非阻塞连接不能立刻获取connect结果,后面可使用select/poll/epoll等对socket 可写性进行判断,如果socket已可写,使用getsockopt(iSocket, SOL_SOCKET, SO_ERROR ,&err, &len)进行判断…好像挺麻烦是不是,但是我还是建议在大部分项目中connect前设置非阻塞(小工具之类的就无所谓了,项目中一定要保证效率)。如果使用阻塞模式,有可能的问题: 如果是桌面程序,你的程序可能会卡住无响应。如果你单独为connect开一个线程,可能加大资源消耗,特别是需要connect多个对象的时候。如果你使用线程池,非阻塞connect会导致线程处理效率下降。
下面是个非阻塞connect的部分代码, 使用select, 至于poll/epoll请自行搜索代码,跟非阻塞逻辑无关:int fd; //...省略N行代码,socket的初始化,准备服务器信息等 set_nonblocking(fd); int ret = connect(fd, (struct sockaddr*)&serv_addr, serv_len); if (0 == ret) { //连接成功,直接进入正常业务逻辑 } else { if (EINPROGRESS != errno) { //出错,结束 close(fd); return; } //EINPROGRESS 继续判断 } //使用select对fd进行可写判断 struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; fd_set sets; FD_ZERO(&sets); FD_SET(fd, &sets); ret = select(fd + 1, NULL, &wds, NULL, &tv); if (ret > 0) { if (FD_ISSET(fd, &sets)) { int err = -1; socklen_t len = sizeof(int); ret = getsockopt(fd, SOL_SOCKET, SO_ERROR ,&err, &len) if (ret < 0 || err ) { close(fd); return; } //连接成功 //其实个人觉得如果可写后需要直接send,不需要getsockopt直接通过send也可以知道结果 } } else if (ret == 0) { //超时逻辑 } else { //select出错逻辑 }
为什么要同情京东阿里腾讯裁掉的人?这跟他赚的钱有什么关系?无论他赚一千还是十万,只要不掌握生产材料,不都是无产阶级?不都是依靠劳动来赚取财富的?反而是你这种分化人民的言论,到底是什么意思?你这不典型的资本家分化劳动
iPhone14曝光,这是惊喜还是惊吓?按照惯例,苹果将会在9月正式发布新一代iPhone手机,虽然现在距离9月还有大半年时间,但凭借着iPhone13出色的性能独特的优势,网上对iPhone讨论的声音就没有停止过,可以
科技大爆发的脚步越来越近大变革的时代,为科技大爆发扫清障碍。仔细看这个世界,不管是原始社会会资本社会,其实都是在适应着从石器时代青铜器时代工业革命时代的科技进步的步伐,科技决定政治结构,政治结构决定经济和
安徽ETC停止提供微信支付服务,因手续费不断上涨证券时报网3月29日讯,安徽交通一卡通控股有限公司日前发布关于暂停提供微信支付服务的公告。公告指出,鉴于微信支付手续费不断上涨,已超出该公司的运营承受能力,经与腾讯积极协商无果,该
ipadAir3不开机维修常见故障分析解答年份2019年容量64GB256GB型号后盖上是A2152是wifi版本,无线局域网蜂窝网络机型上是A2123A2153iPadAir(第3代)无线局域网蜂窝网络(仅限中国大陆)机
大厂出海,又打起来了图片来源视觉中国文深燃,作者李秋涵,编辑魏佳出海,是眼下互联网大厂的关键词之一。这倒不是各互联网大厂今年才开始的业务,只是在国内互联网流量见顶的环境下,出海正占据越来越重要的位置。
Mybatis分页插件PageHelper的使用PageHelper分为4。x和5。x并且两者是不兼容的PageHelper的4。x版本和5。x版本在xml中的配置也不相同一配置mybatis分页插件的配置可以在mybaits的
港股开盘3月29日,港股高开,恒指涨0。4,国指涨0。46,恒生科技指数涨0。96。盘面上,大型科技股多数上涨,百度涨超2,美团阿里巴巴京东腾讯皆涨超1,快手网易小幅低开苹果概念股汽车股体
终止合作!微信支付又摊事了?点关注,不迷路!快来跟支付二哈一起聊支付吧!欢迎关注支乎公众号,了解更多行业资讯!微信支付又出问题了?不久之前,安徽ETC宣布停止微信支付一事在社交平台上引发热议。全文1372字,
你怀念微信里消失的漂流瓶吗?我只记得QQ里有漂流瓶,真的不清楚微信里也会有。神秘的漂流瓶啊!你如今在哪里?10年前,我虽然离异,但仍然摆脱不了前夫的骚扰和纠缠。无论在哪个单位打工,他都会像幽灵一样,莫名其妙地
砍一刀砍掉的是消费者的信任前不久,某主播在直播间发起拼多多砍一刀换手机活动,号召粉丝一起参与,直播间几万名观众砍到了小数点后六位却依然未能换购成功。可谓暴露出了拼多多砍一刀这一促销手段欺骗消费者的本质。这种