【问题标题】:How to use select() on sockets properly?如何在套接字上正确使用 select()?
【发布时间】:2015-12-19 02:46:05
【问题描述】:

我正试图在套接字上调用select,但我不明白我做错了什么。

setup_server_socket 调用 bindlisten 并将套接字设置为非阻塞模式。

select 调用上的以下代码块似乎没有前进到FD_ISSET。我尝试连接客户端,似乎成功了,但 select 从未返回任何内容。

这样做的正确方法是什么?

...
int listenfd = setup_server_socket( serverPort );

if( -1 == listenfd )
    return 1;

fd_set read_fds;
FD_ZERO(&read_fds);
int fdmax = listenfd;


// loop forever
while( 1 )
{
    if (select(fdmax+1, &read_fds, NULL,NULL,NULL) == -1){
        perror("select");
        exit(4);
    }

    for (int i = 0; i<= fdmax; i++){
        printf("Testing: %d, %d\n", i, FD_ISSET(i,&read_fds));

    }return 0;
...

【问题讨论】:

  • 来自精美手册:... If time‐ out is NULL (no timeout), select() can block indefinitely. ... BTW:从 select() 返回 -1 不是错误
  • 当然,但仅仅因为它可以并不意味着它应该......我遇到的问题是 FD_ISSET 从未返回任何东西,尽管客户端已连接。但那是因为我没有先使用FD_SET。
  • 另外:你必须在调用 select() 之前重新初始化 read_fds 每次(select 改变 fd_sets )。大多数情况下,这是通过从另一个 fd_set 复制来完成的。
  • 甜,我想我现在开始明白了。
  • 并且,在从 select 返回 -1 后,你应该检查 errno,尤其是 EINTR/EAGAIN/EWOULDBLOCK 不应被视为错误。

标签: c sockets select


【解决方案1】:

多次阅读syscalls(2)select(2)select_tut(2)poll(2)errno(3)(顺便说一句,你应该更喜欢poll(2)而不是过时的select,它不能处理大于__FD_SETSIZE,即我的 Linux/Debian/x86-64 系统上的 1024)。

然后:

fd_set read_fds;
FD_ZERO(&read_fds);
int fdmax = listenfd;
FD_SET(listenfd, &read_fds);

应该在调用select之前进入while(1)循环。顺便说一句,我建议使用poll 而不是select

另请阅读C10k problem

不要忘记select 正在更改其给定的fd_set-s(并且通常希望它们不为空)...

也许使用strace(1)gdb(1)。如果您使用GCC 编译,请将其调用为gcc -Wall -Wextra -g 以获取更多警告和调试信息。如果您使用Clang 编译您的C 代码,请考虑使用Clang Static Analyzer

也阅读Advanced Linux Programming(这是一本免费的书,您也可以在纸上阅读,或从其他几个地方下载,例如this 等...)

【讨论】:

  • 抱歉,您的意思是我应该只参考手册页吗?我已经尽我所能做到了:(
  • 我的意思是我确信这些链接很好,但我想在跑到这里之前先走路……另外,Advanced Linux Programming 上的链接断开了
  • 它曾经是一个工作链接,它是一本你可以在纸上阅读的书。
【解决方案2】:

这是一个使用 select() 的(修剪过的)示例

INT32    selectStatus;                                 /* select() return code */

char     tempreport[ 256 ] = {'\0'};

struct   timeval tv;

fd_set   fdread;
//fd_set   fdwrite;
//fd_set   fdexcep;



// note:
//  must try to read report until no report available 
// so have latest report in buffer
do
{
    /* Note: timeout must be (re)set every time before call to select() */
    tv.tv_sec = 1;
    tv.tv_usec = 0;


    FD_ZERO(&fdread);
    FD_SET( FD, &fdread );

    selectStatus = select(FD+1, &fdread, NULL, NULL, &tv);

    switch( selectStatus )
    {
        case -1:
            ....
            break;

        case 0:
            // timeout, I.E. nothing to read
            ....
            break;

        default: /* available to read */

            memset(tempreport, 0x00, sizeof(tempreport) );
            readStatus = read_UDP_socket( FD, tempreport, sizeof(tempreport), &readCount );

            break;
    } // end switch( selectStatus )
} while( (0 < selectStatus)&&(eRS_Success == readStatus ) );
 // exit loop on select timeout or select error or read failure

注释:

eRS_Success is an entry from an enum used for various return codes
read_UDP_socket() is a local function

【讨论】:

    猜你喜欢
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多