未命名的unix域套接字具有错误的地址长度

时间:2018-06-11 15:40:19

标签: c linux

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

  

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

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

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

2 个答案:

答案 0 :(得分:2)

你有未命名的未绑定的Unix域数据报套接字tx_sock,以及绑定到rx_sock中的抽象地址的Unix域数据报套接字bind_path

注意:根据man 7 unix,Linux中未绑定的Unix域套接字未命名,其地址长度为sizeof (sa_family_t)。根据POSIX.1,unbound socket addresses are unspecified,所以我们真的需要注意我们对未绑定套接字地址及其长度的期望。在这种特殊情况下,在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_addrrecv_addrlen中收到该地址。

(我使用四个线程的测试程序验证了这种行为,其中每个线程向另一个线程发送消息,并打印所有收到的消息以及它们来自哪里,使用标准地址和抽象地址。一切都按照记录的方式工作。)

答案 1 :(得分:-2)

当然,你的名字长度为零。

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

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

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

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