前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Socket编程中的几点问题总结

Socket编程中的几点问题总结

作者头像
扫帚的影子
发布2020-06-22 15:53:43
2K0
发布2020-06-22 15:53:43
举报
文章被收录于专栏:分布式系统进阶
Socket编程中的几点问题总结
epoll_ctl中 epoll_event参数设置
  • 对于 EPOLLERREPOLLHUP,不需要在epoll_event时针对fd作设置,一样也会触发;
  • EPOLLRDHUP实测在对端关闭时会触发,需要注意的是:
    1. EPOLLRDHUP的处理应该放在EPOLLINEPOLLOUT前面,处理方式应该 是close掉相应的fd后,作其他应用层的清理动作;
    2. 如果采用的是LT触发模式,且没有close相应的fd, EPOLLRDHUP会持续被触发;
    3. EPOLLRDHUP想要被触发,需要显式地在epoll_ctl调用时设置在events中;
    4. 对端关闭包括:ctrl + c, kill, kill -9。
  • 对于EPOLLOUT
    1. 有写需要时才通过epoll_ctl添加相应fd,不然在LT模式下会频繁触发;
    2. 对于写操作,大部分情况下都处于可写状态,可先直接调用write来发送数据,直到返回 EAGAIN后再使能EPOLLOUT,待触发后再继续write
accept相关:
  • accept接收对端连接,会触载EPOLLIN, 这里可以循环多次调用accept, 直至返回 EAGAIN, 同时适用于LT和ET。
对已经close的fd继续操作
  • read: 返回-1, errno = 9, Bad file descriptor ;
  • close: 同上;
  • write:同上;
如何判断对端关闭
  • 优先使用上面介绍的EPOLLRDHUP;
  • 使用EPOLLIN, 然后调用read, 此时返回的ssize_t类型结果为0;
  • 对端关闭包括:ctrl + c, kill, kill -9。
对端正常 close时本端行为

这部分有些内容上面已阐述过,这里统一归纳一下。

  • 对端close时,如果接收缓冲区内已无数据,则走tcp四次挥手流程,发送FIN 包,此时本端会触发事件如下: EPOLLRDHUP (需要主动在epoll_ctal时加入events) EPOLLIN EPOLLOUT
    1. 此时应优先处理EPOLLRDHUP,它明确表明对端已经关闭,处理时close相应fd后,无需再继续处理其他事件;
    2. 如果不处理EPOLLRDHUP的话,也可以处理EPOLLIN事件,此时read返回0, 同样表明对端已经关闭;
    3. 如果以上两个事件都没有处理,而是在EPOLLOUT事件里又向fd写了数据,数据只是写入到本地tcp发送缓冲区,此时write调用会返回成功,但是紧接着epoll_wait又会返回如下事件组合: EPOLLERR EPOLLHUP EPOLLIN EPOLLOUT POLLRDHUP (需要主动在epoll_ctal时加入events) 可以看到相比之前多了EPOLLERREPOLLHUP,是因为之前收到了对端close时发送的FIN 包,此时再给对端发送数据,对端会返回RST包。 如果在收到RST包后,又向对端发送数据,会收到sigpipe异常,其默认处理是终止当前进程,此时可通过忽略此异常解决,忽略后write会返回-1, erron =32, Broken pipe: signal(SIGPIPE, SIG_IGN); Broker pipie这个异常,说到底是应用层没有对相应的fd在收到对端关闭通知时,作正确的处理所致,它并不是tcp/ip通讯层面的问题。
    4. 下图可以看到发送了FIN

tcp_fin.png

  • 对端close(kill, kill -9)时,如果接收缓冲区内还有数据,不会发送FIN包,而是发送RST,此时本端:
代码语言:javascript
复制
1. 收到`RST`后的第一次写操作,写失败,errno = 104,  Connection reset by peer; 之后将触发下列事件:

   ```
   EPOLLIN
   EPOLLOUT
   EPOLLHUP
   EPOLLRDHUP(需要主动在epoll_ctal时加入events)
   ```

   

2. 收到`RST`后的第二次及后序的写操作,写失败,在忽略了`SIGPIPE`后,erron =32, Broken pipe;

3. 收到`RST`后的读操作:errno = 104,  Connection reset by peer

4. 下面可以看到发送了`RST`包:

tcp_rst.png

阻塞与非阻塞
  • 针对Epoll的LT模式,socket fd可以设置成阻塞也可以设置成非阻塞;
  • 针对Epoll的ET模式,socket fd只能设置成非阻塞;
    1. ET状态有变化才触发,因此在收数据时必须循环读取,收尽当前可收数据。因为不知道下一次调用read时还有没有数据,一旦没有数据,又没有用非阻塞方式,则将一直阻塞在read调用上;
    2. 当然如果在LT模式下也每次循环读取,也有类似的问题;
    3. 采用非阻塞循环读取方式时,如果当前socket fd上恰好有持续大数据量写入,则这个循环读取可能持续较长时间,从而导致其他socket fd上的读写操作将被延迟。针对这种情况,我们只能是控制当前socket fd上的读操作,并将其保存,在下一次event loop中不依赖ET的触发,直接针对保存的fd继续其读操作。
close行为
  • close时,如果接收缓冲区还有数据未read到应用层,则不会走四次挥手流程,直接发RST包,这个前面已经介绍过;
  • close时,如果发送缓冲区还有数据未发送,close立即返回,系统接管这个socket, 将尽力将发送缓冲区数据到对端,然后走发送FIN包;
  • 使用SO_LINGER改变close默认行为: 通过struct linger设置 linger.l_onoff linger.l_linger close行为 kernel行为 备注 0 为 disable 忽略 立即返回,同close的默认行为 尽力将发送缓存区中数据发送到对端,然后发送FIN包,四次挥手 > 0 为enable 0 立即返回 不走正常四次挥手,直接发送RST包,没有TIME_WAIT状态 > 0 为enbale 大于0 不管socket是否为blocking或noblocking, 都会阻塞直数据发送完成并收到对端的ACK, 或者linger.l_linger超时 如超时不走正常四次挥手,直接发送RST包,没有TIME_WAIT状态
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Socket编程中的几点问题总结
    • epoll_ctl中 epoll_event参数设置
      • accept相关:
        • 对已经close的fd继续操作
          • 如何判断对端关闭
            • 对端正常 close时本端行为
              • 阻塞与非阻塞
                • close行为
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档