0

我有一个单线程程序。它每五秒向四个目的地发送消息。我不想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 秒。
4

3 回答 3

4

是的,select设计的前提是您希望在每个套接字准备就绪后立即对其进行服务。

如果我了解您要做什么,那么完成它的最简单方法是在 fdset 准备好时从 fdset 中删除每个套接字。如果集合中还有剩余的套接字,使用gettimeofday向下调整超时时间,然后select再次调用。当集合为空时,所有四个插槽都可用,您可以继续。

于 2013-04-27T22:27:57.230 回答
1

有以下三种基本方法:

如果你想保持严格的可移植性,你需要迭代:

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

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

  • 启动 n 个线程
  • 每个线程选择一个 fd
  • 加入所有线程

如果您不需要便携:大多数操作系统都有针对这种情况的工具,例如 Windows/.NET 具有 WaitAll(以及异步发送和事件)

于 2013-04-27T22:40:39.710 回答
0

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

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

于 2013-04-27T22:25:02.693 回答