【Linux】Linux select多路复用socket一直返回-1(select的bug)

作者:李春港
出处:https://www.cnblogs.com/lcgbk/p/14776820.html

一、前言

最近遇到了一个很奇怪的问题,代码逻辑是这样:我使用Linux的socket进行TCP连接通信,客户端在一个独立的线程间隔5s时间不停给服务端发送心跳,服务端也会根据心跳回应。如果数据接收线程在8s内都没有接收到任何数据,则close(socket)关闭套接字。数据接收:在数据接收线程使用了select多路复用机制,对socket是否有数据到来进行监听。

二、问题

当关闭套接字的时候,数据接收线程发现select函数一直返回有文件描述符有数据到来,但是实际读取socket套接字文件描述符的时候发现一直返回-1。

三、原因

这个原因我们可以通过Linux的手册查看select的说明即可知道答案,在Linux终端使用 man select 查看,我发现一下这一段内容:

Multithreaded applications
If a file descriptor being monitored by select() is closed in another thread, the result is unspecified. On some UNIX systems, select() unblocks and returns, with an indication that the file descriptor is ready (a subsequent I/O operation will likely fail with an error, unless another the file descriptor reopened between the time select() returned and the I/O operations was performed). On Linux (and some other systems), closing the file descriptor in another thread has no effect on select(). In summary, any application that relies on a particular behavior in this scenario must be considered buggy.

译文:

多线程应用程序
如果select()监视的文件描述符在另一个线程中被关闭,则结果是未指定的。 在某些UNIX系统上,select()解除阻塞并返回,并指示文件描述符已经就绪(后续的I/O操作可能会失败并出现错误,除非在返回select()和执行I/O操作之间重新打开文件描述符)。 在Linux(和其他一些系统)上,在另一个线程中关闭文件描述符对select()没有影响。 总之,在这个场景中,任何依赖于特定行为的应用程序都必须被认为是有bug的。

由select的手册得知,这是select函数的一个bug,所以我们在使用select的时候需要注意。

四、解决方案

思路:

  1. 如果在select多路复用一直返回成功,但是实际读取文件描述符数据错误的时候,就关闭socket,数据接收线程先不要再去监听这个描述符;
  2. 去告诉心跳线程不要再发送心跳并且重连服务端;
  3. 心跳线程发现接收线程发过来的重连信号,则停止发送心跳并且在一定的时间间隔去重连服务端;
  4. 心跳线程重连服务端成功,告诉数据接收线程socket已经成功连接服务端,可以继续监听socket描述符。
原文地址:https://www.cnblogs.com/lcgbk/p/14776820.html