1

的手册connect说:

如果套接字 sockfd 是 SOCK_DGRAM 类型,那么 addr 是默认发送数据报的地址,也是接收数据报的唯一地址。

下面的 C 程序似乎揭示了UNIX 域套接字上connect/的 Linux 实现中的一个错误recv,因为它从一个地址 ( @"PWE0") 而不是它连接到的地址 ( ) 接收到一条消息@"STAR",然后终止。与此相反,我期望的行为是程序不会终止,因为它忠实地等待来自它所连接的地址的消息,而该地址永远不会到达。(我故意没有用返回值检查和打印语句来混淆这个例子,因为它可以很容易地用strace; 在现实生活中进行检查,调用将由不同的进程进行。)

#include <sys/socket.h>

char header[] = "PLD ";

int main()
{
#define addrlen sizeof(unsigned short) + sizeof (char [1+4]) + sizeof(int)
    int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
       bind(fd, &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
    int gd = socket(PF_UNIX, SOCK_DGRAM, 0);
       bind(gd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
    int hd = socket(PF_UNIX, SOCK_DGRAM, 0);
       bind(hd, &(struct sockaddr){ AF_UNIX, "\0PWE0" }, addrlen);
    sendto(hd, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0,
                &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
    connect(fd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
    char buf[32];
    return recv(fd, buf, sizeof buf, 0);
}

那么,Linux [以及 HP-UX(带有文件系统路径名)] 是否违反了规范,还是我遗漏了什么?

4

1 回答 1

2

编辑我注意到我的第一个答案是错误的。所以我编辑了很多文本。

EDIT2修复了考虑到正在使用抽象地址空间的 addrlen。代码的行为保持不变。

你的定义addrlen有点莫名其妙。

  1. 您似乎只为地址路径保留 4+1 个字节。我想,“\0|P|L|D||\0”是 6 个字节。

  2. 从哪里来sizeof(int)?我不明白你的意思。

但是,我认为 sendto 通过的原因是sendto()and的顺序connect()。如果您在 sendto 之前移动连接,您将获得预期的行为。Unix 套接字缓冲数据报。发送的数据报在连接“开始过滤”之前通过。在交换连接和发送到的情况下,您可以看到数据报在被操作系统(来自发送方)接收时被过滤。当用户空间应用程序调用 recv 时,似乎没有更多的过滤。

Here is an strace output (last lines only) of the modified program.
socket(PF_FILE, SOCK_DGRAM, 0)          = 3
bind(3, {sa_family=AF_FILE, path=@"PLD "}, 8) = 0
socket(PF_FILE, SOCK_DGRAM, 0)          = 4
bind(4, {sa_family=AF_FILE, path=@"STAR"}, 8) = 0
socket(PF_FILE, SOCK_DGRAM, 0)          = 5
bind(5, {sa_family=AF_FILE, path=@"PWE0"}, 8) = 0
connect(3, {sa_family=AF_FILE, path=@"STAR"}, 8) = 0
sendto(5, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0, {sa_family=AF_FILE, path=@"PLD "}, 8) = -1 EPERM (Operation not permitted)
recvfrom(3, ^C <unfinished ...>

我改变了在函数调用中定义地址的简洁方式。这是我修改后的代码:

#include <sys/socket.h>
#include <sys/un.h>
int main()
{
    const char hdr [] = "\0PLD ";
    socklen_t addrlen =  sizeof(sa_family_t) + sizeof(hdr);

    int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
    bind(fd, &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);
    int gd = socket(PF_UNIX, SOCK_DGRAM, 0);
    bind(gd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
    int hd = socket(PF_UNIX, SOCK_DGRAM, 0);
    bind(hd, &(struct sockaddr){ AF_UNIX, "\0PWE0" }, addrlen);

    connect(fd, &(struct sockaddr){ AF_UNIX, "\0STAR" }, addrlen);
    sendto(hd, "PWE0\0\0\0\n\1h\0\0\0\1\0\0\0\1", 18, 0,
           &(struct sockaddr){ AF_UNIX, "\0PLD " }, addrlen);

    char buf[32];
    return recv(fd, buf, sizeof buf, 0);
}

我承认这有点莫名其妙。必须有一些与addrlen参数有关的未定义行为bind(),因为如果我只将原始代码 addrlen 更改为 sizeof(struct sockaddr_un),我会得到不同的行为,其中 connect() 被拒绝并且 recv 块(可能是我修改过的代码有一个我没有看到的错误)。

于 2013-05-24T11:11:50.207 回答