【问题标题】:UNIX domain datagram socket blocks on recvfrom() with select()使用 select() 在 recvfrom() 上的 UNIX 域数据报套接字块
【发布时间】:2020-09-08 17:11:16
【问题描述】:

我正在使用 UNIX 域数据报套接字将记录从多个客户端发送到多线程程序中的单个服务器。一切都在一个过程中完成;我将记录从多个线程发送到充当服务器的单个线程。所有线程都使用它们的关联掩码分配给单独的核心。

我的问题是当我使用 select() 从在套接字缓冲区中有记录的客户端套接字中检索记录时。我正在使用与单个客户端套接字相同的基本设置(并且它在该上下文中工作),但现在当我调用 recvfrom 时它挂起(显然它阻塞)。这很令人惊讶,因为 select() 函数已经将套接字标识为可供读取。

int select_clientsockets(int64_t srvrfd, int64_t * claddr, int fds_array[], int fd_count, void * recvbuf){

    int fds_ready;
    int abc;
    int64_t cli_addr;

    FD_ZERO(&fdset);
    FD_SET(0,&fdset);

    socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);

    fds_ready = select(3, &fdset, NULL, NULL, 0);

    for (int i = 0; i < fd_count; i++){

        fds_array[i] = 0;

        if (FD_ISSET(i, &fdset)) {
            fds_array[i] = 1;
            cli_addr = claddr[i];
            server_receive(srvrfd, recvbuf, 720, cli_addr);}
    }

    return 0;
}

select 函数在 select 表示数据可用的客户端上调用 server_receive:

int64_t server_receive(int64_t sfd, void * buf, int64_t msgLen, int64_t claddr)
{
    socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
    int numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) claddr, len);

    if (numBytes == -1)
        return 0;

    return numBytes;
}

客户端套接字地址取自 3 元素数组“claddr”(用于 3 个客户端套接字),其中在创建套接字时填充每个客户端套接字的对应位置。在创建套接字时,我还调用 FD_SET 将客户端地址设置到 fd_set 中。我想我应该从 fd_set 获取客户端套接字地址,但是它们都是相同的指针值,所以我不知道为什么会有所作为。对于互联网域数据报套接字,我们可以使用 getpeername() 但我不知道 UNIX 域套接字是否有类似的功能——或者即使这就是问题所在。

非常感谢您对此提供的任何帮助。

更新:

客户端 fds 在创建套接字时被添加到全局 fdset 结构中:

int64_t * create_socket_client(struct sockaddr_un claddr, int64_t retvals[])
{
    int sfd, j;
    size_t msgLen;
    ssize_t numBytes;
    char resp[BUF_SIZE];

    retvals[0] = 0;
    retvals[1] = 0;

    sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sfd == -1)
        return retvals;

    memset(&claddr, 0, sizeof(struct sockaddr_un));
    claddr.sun_family = AF_UNIX;
    snprintf(claddr.sun_path, sizeof(claddr.sun_path),  "/tmp/ud_ucase_cl.%ld", (long) getpid());

    FD_SET(sfd,&fdset);

    retvals[0] = sfd;
    retvals[1] = (int64_t)&claddr;

     return retvals;
}

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 您是否在线程之间共享套接字描述符?是否有可能 select 为您提供了一个准备好读取到同一套接字描述符上的 select(2) 的两个不同线程的描述符,但是当第二个线程使用它时,另一个线程已获取数据包?

标签: c linux sockets


【解决方案1】:
FD_ZERO(&fdset);
FD_SET(0,&fdset);

socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);

fds_ready = select(3, &fdset, NULL, NULL, 0);

for (int i = 0; i < fd_count; i++){
    fds_array[i] = 0;

    if (FD_ISSET(i, &fdset)) {

您的代码清空fdset,然后仅将0 添加到fdset。因此,当您调用 select 并将其传递给 fdset 时,您只是要求它检查套接字 0 是否准备就绪。

您稍后检查0 到小于fd_count 的套接字是否在fdset 中,但只有零可能是因为它是您询问的唯一一个。

您要检查是否准备就绪的套接字列表在哪里?

【讨论】:

  • 当我看到模板示例时,我很困惑为什么要清空 fdset——我已经填充了 fdset。在这种情况下,客户端套接字 fds 的列表是 5、6 和 7,正如 Barmar 上面显示的那样,我(错误地)发送了 0、1 和 2,这是不正确的。
  • 也许您想制作全局 fd_set 的本地副本而不是归零 fdset
  • Barmar 说我应该制作一个本地副本,因为它已被 select() 就地修改了——所以我会这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-18
  • 2022-11-24
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多