首页 > 解决方案 > 套接字编程:是什么导致 select() 系统调用在当前线程执行时不返回?

问题描述

我在多套接字应用程序上调用select()时遇到问题。

这是它应该如何工作的。

Writer 写入:[0010]HelloWorld在套接字上,前 4 个字符始终是表示有效负载大小的数字。

读者应该做到以下几点:

  1. 调用select()以验证给定套接字是否可读,然后是read前 4 个字符,将 char 更改为 digit 以获取缓冲区的大小,并分配该大小的缓冲区。
  2. 从套接字复制字符(前 4 个字符之后)并粘贴到缓冲区以进行进一步处理
  3. read再次输入 4 个字符,这应该会失败,并且在无法读取任何数据时,应用程序应该彻底退出程序。

问题出在第三次select通话中。select接下来是read迭代,每次我们检查select()套接字的可读性并且一旦验证,我们就继续进行read. read虽然套接字是有效的并且几乎整个过程都可以正常工作,但除了预期会失败之前的第 3 步的最后一点之外,我select()最后一次调用系统调用,它在调用时完全冻结了线程select

我在网上找不到任何可以解释这种奇怪现象的资源。为了验证线程没有返回,我在进行系统调用之前创建了一个虚拟对象select()并将其记录在销毁中。不幸的是,破坏者永远不会被调用。

源代码是专有的,不能共享。

片段:

fd_set f_set;
int err = 0;
while(check_edge_case())
{
  if(time_out_valid())
  {
     int nfds = GetFileDescriptor();

     FD_ZERO(&f_set);
     FD_SET(nfds, &f_set);

     for_each (clients, [&](client_t &client)
     {
       int fd = client.descriptor;
       if(fd)
       {
         FD_SET(fd, &f_set);
         nfds = std::max(nfds, fd);
       }
     });
    
    ++nfds;
    
    // perform select
    if(time_out_valid())
    {
      struct timeval_t time_val = GetTimeOut();
      err = select(nfds, &f_set, 0,0, &time_val);
    }
    else
    {
      err = select(nfds, &f_set, 0, 0, 0); // blocks in this statement
    }
    
    // check for error
    if(!err)
    {
      err = 0;
      continue;
    }
    else if (err = -1 && errno == EINTR)
    {
      err = 0;
      continue;
    }
    else if (err < 0)
    {
      retunr errno;
    }
    
    if(FD_ISSET(GetFileDescriptor(), &f_set))
    {
      return ECANCELED;
    }
    
    // further processing for read operation
    bool executed = false;
    for_each (clients, [&](client_t &client)
     {
       int fd = client.descriptor;
       if(FD_ISSET(fd, &f_set))
       {
         if(client.f_read) err = client.f_read();
         else err = 0;
         
         executed = true;
         break;
       }
     });
    
    if(!found) return ENODATA;
    
    
  }
}

标签: c++macossockets

解决方案


您将错误的timeout参数传递给您的第二次调用select,因此是阻塞的。

从文档中:

如果结构的两个字段timeval都为零,则then select()立即返回。(这对于轮询很有用。)

如果timeout指定为NULL,则select()无限期地阻塞等待文件描述符准备就绪。

所以传递一个归零timeval结构的地址,而不是0,你应该没问题。


推荐阅读