0

我在线程中使用 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。因此,例如,我无法在不重新启动线程的情况下重新启动发送方(以正确的顺序。)

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

4

2 回答 2

4

Run线程只构造一次read_fds

select调用更新以清除所有未准备好数据的描述符的read_fds所有位,并为之前设置且已准备好数据的描述符设置所有位。

因此,如果没有描述符准备好任何数据并且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接口在某些方面更容易使用。)

于 2012-04-21T07:44:22.060 回答
0

你期望它的表现如何?的要点select()是休眠到超时,直到有数据可供读取;在这种情况下,它将在 1 秒后超时并返回 0。也许您实际上并不希望在流开始之前超时?

于 2012-04-20T22:38:37.820 回答