Linux:Unix域数据报套接字不遵循connect / recv规范,是吗?

时间:2013-05-24 07:31:07

标签: sockets unix ipc datagram unix-socket

connect的手册说:

  

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

以下C程序似乎在UNIX域套接字上显示connect / recv的Linux实现中的错误,因为它从除了@"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(带有文件系统路径名)]是否违反规范,或者我错过了什么?

1 个答案:

答案 0 :(得分:2)

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

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

您对addrlen的定义有点莫名其妙。

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

  2. sizeof(int)来自哪里?我无法弄清楚你的意思。

  3. 但是,我认为sendto经历的原因是sendto()connect()的顺序。如果在sendto之前移动连接,则会得到预期的行为。 Unix套接字缓冲数据报。在连接“开始过滤”之前,发送的数据报将通过。在交换connect和sendto的情况下,您可以看到数据报在被OS(来自发送方)接收时被过滤。当用户空间应用程序调用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的{​​{1}}参数相关的一些未定义行为,因为如果我只将原始代码addrlen更改为sizeof(struct sockaddr_un),我会得到一个不同的行为,其中connect()是拒绝和recv阻止(也许我修改过的代码有一个我没看到的错误。)