简体   繁体   English

带有 select() 的 recvfrom() 上的 UNIX 域数据报套接字块

[英]UNIX domain datagram socket blocks on recvfrom() with select()

I am using UNIX domain datagram sockets to send records from multiple clients to a single server in a multithreaded program.我正在使用 UNIX 域数据报 sockets 将记录从多个客户端发送到多线程程序中的单个服务器。 Everything is done within one process;一切都在一个过程中完成; I'm sending records from multiple threads to a single thread that acts as the server.我将记录从多个线程发送到充当服务器的单个线程。 All threads are assigned to separate cores using their affinity masks.所有线程都使用它们的关联掩码分配给单独的核心。

My problem is when I use select() to retrieve records from client sockets that have records in the socket buffer.我的问题是当我使用 select() 从客户端 sockets 检索在套接字缓冲区中有记录的记录时。 I am using the same basic setup I used with a single client socket (and it worked in that context), but now it hangs (apparently it blocks) when I call recvfrom.我正在使用与单个客户端套接字相同的基本设置(并且它在该上下文中工作),但现在当我调用 recvfrom 时它挂起(显然它阻塞)。 That's surprising because the select() function has already identified the socket as available for reading.这很令人惊讶,因为 select() function 已经将套接字识别为可供读取。

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;
}

The select function calls server_receive on clients where select says data are available: select function 在 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;
}

The client socket address is taken from the 3-element array "claddr" (for 3 client sockets) where the corresponding position for each client socket is filled in when the socket is created.客户端套接字地址取自 3 元素数组“claddr”(用于 3 个客户端套接字),其中每个客户端套接字对应的 position 在创建套接字时填充。 At socket creation I also call FD_SET to set the client address into the fd_set.在创建套接字时,我还调用 FD_SET 将客户端地址设置到 fd_set 中。 I think I should get the client socket address from fd_set instead, BUT they're both the same pointer value so I don't know why that would make a difference.我想我应该从 fd_set 获取客户端套接字地址,但是它们都是相同的指针值,所以我不知道为什么会有所作为。 For internet domain datagram sockets we can use getpeername() but I don't know if there is an analogous function for UNIX domain sockets -- or even if that's the problem. For internet domain datagram sockets we can use getpeername() but I don't know if there is an analogous function for UNIX domain sockets -- or even if that's the problem.

Thanks very much for any help with this.非常感谢您对此的任何帮助。

UPDATE:更新:

Client fds are added to the global fdset struct on socket creation:客户端 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;
}
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)) {

Your code empties fdset then adds only 0 to fdset .您的代码清空fdset然后仅将0添加到fdset So when you call select and pass it fdset , you are asking it only to check socket 0 for readiness.因此,当您调用select并将其传递给fdset时,您只是要求它检查套接字0是否准备就绪。

You later check if sockets 0 to one less than fd_count are in fdset , but only zero could possibly be because it's the only one you asked about.您稍后检查 sockets 0fd_count小一是否在fdset中,但只有零可能是因为它是您询问的唯一一个。

Where is the list of sockets you want to check for readiness?您要检查准备情况的 sockets 列表在哪里?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM