选择多个非阻塞连接

时间:2013-04-27 22:20:15

标签: c sockets

我有一个单线程程序。它每五秒钟向四个目的地发送消息。我不希望connect()被阻止。所以我正在写这样的程序:

int              j, rc, non_blocking=1, sockets[4], max_fd=0;
struct sockaddr  server=get_server_addr();
fd_set           fdset;
const struct timeval  conn_timeout = { 2, 0 }; /* 2 seconds */

for (j=0; j<4; ++j)
{
    sockets[j]=socket( AF_INET, SOCK_STREAM, 0 );
    ioctl(sockets[j], FIONBIO, (char *)&non_blocking);
    connect(sockets[j], &server, sizeof (server));
}

/* prepare fd_set */
FD_ZERO ( &fdset );
for (j=0;j<4;++j)
{
    if (sockets[j] != -1 )
    {
        FD_SET ( sockets[j], &fdset );
        if ( sockets[j] > max_fd )
        {
             max_fd = sockets[j];
        }
    }
}

rc=select(max_fd + 1, NULL, &fdset, NULL, &conn_timeout );
if(rc > 0)
{
    for (j=0;j<4;++j)
    {
        if(sockets[j]!=-1 && FD_ISSET(sockets[j],&fdset))
        {
            /* send() */
        }
    }
}

/* close all valid sockets */

但是,似乎select()在ONE文件描述符准备就绪后立即返回,而不是阻塞conn_timeout(2秒)。那么在这种情况下我如何实现目标?

  1. 如果所有套接字都准备就绪,程序将继续。
  2. 如果任何一个套接字没有准备好,程序可以在那里阻塞2秒钟。

3 个答案:

答案 0 :(得分:4)

是的,select的设计假设您希望在每个套接字准备就绪后立即为其提供服务。

如果我理解你要做的事情,那么实现它的最简单方法是在fdset准备就绪时从fdset中删除每个套接字。如果设备中还有任何套接字,请使用gettimeofday向下调整超时,然后再次呼叫select。当该集合为空时,所有四个套接字都可用,您可以继续。

答案 1 :(得分:1)

有三种基本方法:

如果您希望保持严格的便携性,则需要进行迭代:

  • 从当前时间和您选择的超时计算结束时间
  • 周期:
  • - 使用那些fds 尚未准备好
  • 创建fdset
  • - 计算等待的最长时间
  • - select()
  • - 记住现在准备好
  • 的那些fds
  • - 如果达到结束时间或所有fds准备就中断
  • 结束周期
  • 现在您已了解准备好的fds和已用时间

如果您希望保持便携性,但可以使用线程

  • 开始n个帖子
  • 选择每个线程一个fd
  • 加入所有主题

如果您不需要携带:大多数操作系统都有适合这种情况的工具,例如: Windows / .NET具有WaitAll(与异步发送和事件一起)

答案 2 :(得分:0)

我没有看到您声明的目标与您声明的问题之间存在关联。你说select()阻塞直到至少有一个套接字准备就绪是正确的,但根据上面的目标#2,这正是你想要的。在所有四个套接字同时就绪之前,你所声明的目标中没有任何关于阻塞的内容。

您还应该注意,套接字几乎总是可以写入,除非发送缓冲区已满,这意味着接收器的接收缓冲区已满,这意味着接收器比发送器慢。所以单独使用select()作为底层写定时器并不是一个好主意。