2

在这个最小示例程序中读取发送 unix 数据报域套接字的地址时,我得到的地址长度为 0,但 unix 域套接字的手册页指定:

unnamed:未使用 bind(2) 绑定到路径名的流套接字没有名称。同样,由 socketpair(2) 创建的两个套接字是未命名的。返回未命名套接字的地址时,其长度为 sizeof(sa_family_t),不应检查 sun_path。

根据此手册页,我期望未命名套接字的地址长度为 2。我是否误解了手册页,手册页是否不适用于 unix 类型的套接字,SOCK_DGRAM或者我只是错误地读取了长度?

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>

const char SOCK_NAME[] = { 0, 't', 'e', 's', 't' };
// or for pathnamed socket
// const char SOCK_NAME[] = "/tmp/test.uds";

const char PAYLOAD[] = "Hello!";

int main() {
    int rx_sock = socket(AF_UNIX, SOCK_DGRAM, 0);

    if (rx_sock < 0) {
        perror("Create RX");
        exit(1);
    }

    int tx_sock = socket(AF_UNIX, SOCK_DGRAM, 0);

    if (tx_sock < 0) {
        perror("Create TX");
        exit(1);
    }

    struct sockaddr_un bind_addr;
    bind_addr.sun_family = AF_UNIX;
    memcpy(bind_addr.sun_path, SOCK_NAME, sizeof(SOCK_NAME));

    socklen_t bind_len = sizeof(sa_family_t) + sizeof(SOCK_NAME);

    if (bind(rx_sock, (const struct sockaddr *)&bind_addr, bind_len) != 0) {
        perror("Bind RX");
        exit(1);
    }

    if (sendto(tx_sock, PAYLOAD, sizeof(PAYLOAD), 0, (const struct sockaddr *)&bind_addr, bind_len) < 0) {
        perror("Sendto");
        exit(1);
    }

    // For pathnamed socket
    // unlink(SOCK_NAME);

    struct sockaddr_un recv_addr;
    socklen_t recv_len = sizeof(recv_addr);
    char buffer[1024];

    ssize_t rx_count = recvfrom(rx_sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&recv_addr, &recv_len);

    if (rx_count < 0) {
        perror("Recvfrom");
        exit(1);
    }

    printf("Address size of TX on receiver side: %d\n", recv_len); // 0

    recv_len = sizeof(recv_addr);

    if (getsockname(tx_sock, (struct sockaddr *)&recv_addr, &recv_len) != 0) {
        perror("getsockname");
        exit(1);
    }

    printf("Address size of TX on sender side: %d\n", recv_len); // 2
}
4

2 回答 2

2

您有一个未命名的未绑定 Unix 域数据报套接字tx_sock,以及一个rx_sock绑定到bind_path.

注意:根据man 7 unix,未绑定的 Unix 域套接字在 Linux 中是未命名的,它们的地址长度将为sizeof (sa_family_t). 根据 POSIX.1,未绑定套接字地址是未指定的,因此我们确实需要小心我们对未绑定套接字地址及其长度的期望。在这种特殊情况下,在 Linux 中,使用手册页作为指导,未绑定的 Unix 域数据报套接字没有地址,因此其长度为零。(这甚至是有道理的:零地址长度表示您无法回复发件人。使用未绑定的流套接字,有一个返回发件人的连接,但除了连接本身之外没有其他方式回复发件人;这就是为什么在在这种情况下,地址长度不为零,sizeof (sa_family_t).)

摘要 Unix 域套接字地址是 Linux 的扩展;它们以 NUL 字节 ( ) 开头,\0在文件系统中不可见。

sendto(tx_sock, msg, msg_len, 0, bind_path, bind_path_len)用来发送消息。

您用于recvfrom(rx_sock, buffer, sizeof buffer, 0, &recv_addr, &recv_addrlen)接收消息(recv_addrlen正确初始化为sizeof recv_addr)。

因为tx_sock没有绑定到任何地址,所以它是未命名的未绑定的:它没有地址,无法回复,因此recv_addrlen == 0.

如果您添加代码以绑定tx_sock到某个其他抽象地址,您将在recv_addr和中收到该地址recv_addrlen

(我用一个使用四个线程的测试程序验证了这种行为,其中每个线程向其他线程发送一条消息,并打印所有接收到的消息以及它们来自哪里,包括标准地址和抽象地址。一切都按照文档进行。)

于 2018-06-12T17:45:23.220 回答
-2

当然,您得到的名称长度为零。

char数组中的第一个值是0,立即将字符串终止为零字节长度:

const char SOCK_NAME[] = { 0, 't', 'e', 's', 't' };

unnamed:未使用 bind(2) 绑定到路径名的流套接字没有名称。

不适用,因为您正在使用bind()绑定套接字:

于 2018-06-12T09:13:35.490 回答