【问题标题】:Timeout on an accept socket function接受套接字函数超时
【发布时间】:2016-05-14 09:43:11
【问题描述】:

我试图在接受套接字函数上设置超时但没有成功。我希望接受功能块直到超时延迟结束。 是否可以不将接受功能设置为非阻塞? 我尝试了很多可能性都没有成功。

感谢您的回答。

下面是我的代码:

struct timeval tv;
fd_set readfds;

tv.tv_sec = 1;
tv.tv_usec = 0;

int s, s_remote;
struct sockaddr_un remote;
struct sockaddr_un local;

if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
  perror("socket");
  exit(1);
}

FD_ZERO(&readfds);
FD_SET(s, &readfds);

if (select(s+1, &readfds, NULL, NULL, &tv) > 0) {

  printf("Waiting for a connection...\n");

  memset(&local, 0, sizeof(local));
  local.sun_family = AF_UNIX;
  strcpy(local.sun_path, SOCK_PATH);
  unlink(local.sun_path);

  if (bind(s, (struct sockaddr *)&local, sizeof(local)) == -1) {
    perror("UnixSocketClient :: error bind.\n");
    close(s);
    return -1;
  }

  if (listen(s, 5) == -1) {
    perror("UnixSocketClient :: error listen.\n");
    close(s);
    return -1;
  }

  socklen_t remote_len = sizeof(remote);
  printf("Accept :\n\r");

  if ((s_remote = accept(s, (struct sockaddr *)&remote, &remote_len)) == -1) {
    perror("UnixSocket :: error accept.\n");
    return -1;
  }
  printf("Client accepted\n\r");
}

【问题讨论】:

  • 既然你已经有了select循环设计,为什么不在进入select循环之前调用bindlisten,设置socket非阻塞,使用select知道什么时候打电话给accept? (你的设计在阻塞和非阻塞设计之间已经被破坏了。你为什么要selecting s?)

标签: c sockets unix


【解决方案1】:

您的代码毫无意义。您必须致电:

  • socket()
  • 绑定()
  • 听()
  • 选择()
  • 接受()

按这个顺序。

【讨论】:

  • 在接受之前移动选择解决了我的问题。
【解决方案2】:

在设置了超时的侦听套接字调用 select() 并且仅在 select() 未超时时调用 accept()

更新:

上述方法(实际上与this answer 中的方法相同)确实引入了比赛,正如 cmets 对此答案中所讨论的那样。如果选择返回指示传入连接,则在调用接受之前存在(短)延迟。如果在此延迟期间连接断开,则accept() 将阻塞。

解决此问题的唯一方法是在调用 accept() 时使用非阻塞套接字。


如果要求(无论出于何种原因)是使用阻塞套接字,那么一个可能的解决方案是向accept() 中阻塞的进程发送信号,使其返回值-1 和将errno 设置为EINTR

void handle_sigalarm(int sig)
{
  /* do nothing */
}


  ...

  sigaction(SIGALRM , &(struct sigaction){handle_sigalarm}); /* Setup handler for 
     alarm signal to override the default behaviour which would be to 
     end the process . */

  alarm(3); /* Arranges for a SIGALRM signal to be delivered to the calling
   process in 3 seconds. */

  {
    int accepted_socket = accept(...);
    int errno_save = errno;
    alarm(0); /* Cancel any pending alarm. */
    if (-1 == accepted_socket)
    {
      if (EINTR == errno_save)
      {
        fprintf(stderr, "accept() timed out\n");
      }

      /* handling other errors here. */
    }

    ...

【讨论】:

  • 他说他不想将套接字设置为非阻塞。使用您的方案,如果在他到达 accept 时连接已经消失,他可以永远等待。
  • @EJP 假设有一个传入连接,他从select 返回,但是当他调用accept 时,连接已经消失。他的accept 可以一直等待(或者直到有新的连接出现)。所以要使用这个方法,他必须设置socket非阻塞,这是他特意说不想做的。 select 函数只是当前状态的报告(就像accessstat)——它没有未来的保证。 (这是一个常见的错误,过去曾造成严重的安全隐患。)
  • @symbiont 你误解了它的意思。它说“并且连接正在等待”。不能保证它仍然悬而未决,这很奇怪。 Select 是一个状态报告功能,让它导致挂起的连接保持挂起是很奇怪的。如果它以某种方式导致选择调用要求连接永远保持挂起状态,那将是 Windows 中的一个奇怪的设计缺陷。
  • @symbiont 这很奇怪,因为这意味着即使连接关闭或出错,它仍然必须无限期地保持挂起状态。实现必须跟踪调用状态报告函数的事实是很奇怪的。一个文件系统有可用空间的报告保证未来的写入会成功,这与要求一样奇怪。请注意,保证指的是将来任意的操作。
  • @symbiont 您的测试仅显示您系统上发生的行为。我见过很多次,人们根据他们系统上发生的情况做出假设,然后新版本的操作系统出现,或者其他人有中间的 Winsock 驱动程序或防火墙驱动程序,并且行为不同。依靠状态报告功能来提供未来保证是很疯狂的。正如我所说,“这是一个常见的错误,过去曾导致严重的缺陷,具有重大的安全隐患。”。你可以从过去的错误中学习或不学习——你的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-08
  • 2011-05-11
  • 1970-01-01
相关资源
最近更新 更多