为什么如果数据已经发送,select只显示文件描述符就绪?

时间:2012-04-20 22:33:15

标签: c++ c linux network-programming synchronous

我在线程中使用select()来监视数据报套接字,但除非在线程启动之前将数据发送到套接字,否则select()将继续返回0。

我正在混合一点C和C ++;这是启动线程的方法:

bool RelayStart() {
    sock_recv = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&addr_recv, 0, sizeof(addr_recv));
    addr_recv.sin_family = AF_INET;
    addr_recv.sin_port = htons(18902);
    addr_recv.sin_addr.s_addr = htonl(INADDR_ANY);
    bind(sock_recv, (struct sockaddr*) &addr_recv, sizeof(addr_recv));

    isRelayingPackets = true;

    NSS::Thread::start(VIDEO_SEND_THREAD_ID);

    return true;
}

停止线程的方法:

bool RelayStop() {
    isSendingVideo = false;
    NSS::Thread::stop();
    close(sock_recv);
    return true;
}

该方法在线程中运行:

void Run() {

    fd_set read_fds;
    int select_return;
    struct timeval select_timeout;

    FD_ZERO(&read_fds);
    FD_SET(sock_recv, &read_fds);

    while (isRelayingPackets) {

        select_timeout.tv_sec = 1;
        select_timeout.tv_usec = 0;

        select_return = select(sock_recv + 1, &read_fds, NULL, NULL, &select_timeout);
        if (select_return > 0 && FD_ISSET(sock_recv, &read_fds)) {
            // ...
        }
    }
}

问题是如果在调用RelayStart()之前没有进程已经将UDP数据包发送到端口18902,则select()将始终返回0.因此,例如,我无法在不重新启动的情况下重新启动发送方线程(按照正确的顺序)。

只要首先启动发件人,一切似乎都能正常工作。

2 个答案:

答案 0 :(得分:4)

Run个帖子只构建read_fds一次。

select调用更新read_fds以清除所有未准备好数据的描述符的所有位,并将其所有位设置为之前设置的数据并准备好数据。< / p>

因此,如果没有描述符准备好任何数据并且select调用超时(并返回0),则read_fds中的所有位现在都被清除。通过相同的全零位掩码的其他调用将不扫描文件描述符。

您可以在循环内的每次旅行中重新构建读取集:

while (isRelayingPackets) {
    FD_ZERO(&read_fds);
    FD_SET(sock_recv, &read_fds);
    ...
}

或使用带有位集副本的辅助变量:

while (isRelayingPackets) {
    fd_set select_arg = read_fds;
    ... same as before but use &select_arg ...
}

(或者,当然,有一些非select接口在某些方面更容易使用。)

答案 1 :(得分:0)

你是如何期待它的表现? select()的要点是睡眠到超时,直到可以读取数据为止;在这种情况下,它将在1秒后超时并返回0.也许你真的不希望在流开始之前超时?