在Windows中,Socket select()可以工作,在Linux中则会超时。

7

我正在将一个Windows网络应用程序移植到Linux,但在Linux上使用select调用时遇到了超时问题。以下函数会阻塞整个超时时间,并返回,而我检查了数据包嗅探器后发现客户端已经发送了数据。

int recvTimeOutTCP( SOCKET socket, long sec, long usec )
{
  struct timeval timeout;
  fd_set fds;.

  timeout.tv_sec = sec;
  timeout.tv_usec = usec;
  FD_ZERO( &fds );
  FD_SET( socket, &fds );

  // Possible return values:
  // -1: error occurred
  // 0: timed out
  // > 0: data ready to be read
  cerr << "Waiting on fd " << socket << endl;
  return select(1, &fds, 0, 0, &timeout);
}
5个回答

13

我认为select()的第一个参数应该是socket+1

你真的应该使用另一个名称,因为socket也用于其他事情。通常会使用sock


@jamessan 噢,是的,我就是那个意思 ;) - epatel
好的。int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 其中 nfds 是“三个集合中最高编号的文件描述符加1”。 - jamessan
所以我们可以推断出,在Windows中,select()的实现忽略了第一个参数!太酷了! - Ali Nadalizadeh
4
这里不需要暗示 - 在Microsoft的select()文档中明确说明了:http://msdn.microsoft.com/en-us/library/ms740141%28VS.85%29.aspx - mskfisher

3

来自select的手册页面:

int select(int nfds,
           fd_set* restrict readfds,
           fd_set* restrict writefds,
           fd_set* restrict errorfds, 
           struct timeval* restrict timeout);

每个集合中都会检查前nfds个描述符; 即在描述符集中从0到nfds-1的描述符将被检查。

因此,select的第一个参数应该是socket + 1。

return select(socket + 1, &fds, 0, 0, &timeout);

3
问题在于Linux中的fd_set是一个位数组(最初只是一个int,但是这样你只能监视进程的前16个io)。在Windows中,fd_set是一个套接字数组,并且前面有一个长度(这就是为什么Windows不需要知道要监视多少位的原因)。 poll()函数在Linux中需要一个要监视的记录数组,并且具有比select()更好的其他优点,因此它是更好的选择。
int recvTimeOutTCP( SOCKET socket, long msec )
{
    int iret ;
    struct polldf   sockpoll ;

    sockpoll.fd= socket ;
    sockpoll.events= POLLIN ;

    return poll(& sockpoll, 1, msec) ;
}   

谢谢提供信息,这个 poll() 函数跨平台吗? - Ali Nadalizadeh
嗯,在Linux上,最好使用epoll,最好是在边缘触发模式下。 - Nikolai Fetissov

3

select在Windows上忽略第一个参数。来自MSDN:

C++
int select(
  __in     int nfds,
  __inout  fd_set *readfds,
  __inout  fd_set *writefds,
  __inout  fd_set *exceptfds,
  __in     const struct timeval *timeout
);

参数
nfds [in]
被忽略。nfds参数仅用于与Berkeley sockets的兼容性。 ...

1

select(...) 的第一个参数是要在集合中检查的文件描述符数。你的调用告诉它只查看文件描述符0,这几乎肯定不是socket所设置的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接