Unix域套接字: accept()未设置sun_path

8
我正在使用Unix域套接字进行测试,并且可以在它们之间进行通信,但是,在我的测试程序的服务器端调用accept()时,返回的struct sockaddr_un不包含sun_path

我相当确定,在accept()调用之后,Inet套接字的地址和端口被正确填写了,所以我是在测试程序中做错了什么还是期望结果错误?
我正在运行CentOS 6.2和gcc 4.4.6。
示例代码:
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define NAME "socket"

int main(int argc, char **argv)
{
    int sock, msgsock, rval;
    struct sockaddr_un server, client;
    char buf[1024];

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("opening stream socket");
        exit(1);
    }

    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, NAME);

    if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
        perror("binding stream socket");
        exit(1);
    }

    printf("Socket has name %s\n", server.sun_path);
    listen(sock, 5);

    for (;;) {
        socklen_t len = sizeof(client);
        msgsock = accept(sock, (struct sockaddr *)&client, &len);

        if (msgsock == -1)
            perror("accept");
        else do {
            printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));

            bzero(buf, sizeof(buf));
            if ((rval = read(msgsock, buf, 1024)) < 0)
                perror("reading stream message");
            else if (rval == 0)
                printf("Ending connection\n");
            else
                printf("-->%s\n", buf);
        } while (rval > 0);

        close(msgsock);
    }
    close(sock);
    unlink(NAME);

    return 0;
}

client.c

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

#define DATA "Half a league, half a league . . ."

int main(int argc, char **argv)
{
    int sock;
    struct sockaddr_un server;

    if (argc < 2) {
        printf("usage:%s <pathname>", argv[0]);
        exit(1);
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("opening stream socket");
        exit(1);
    }

    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, argv[1]);

    if (connect(sock, (struct sockaddr *) &server,
                sizeof(struct sockaddr_un)) < 0) {
        close(sock);
        perror("connecting stream socket");
        exit(1);
    }

    if (write(sock, DATA, sizeof(DATA)) < 0)
        perror("writing on stream socket");

    close(sock);

    return 0;
}

再次强调问题:

为什么在服务器上进行accept()调用后没有填写sun_path


真是个奇迹……len变成了2,这是sizeof(sa_family_t)的大小。根据文档,只有当套接字未绑定时才会发生这种情况,但在您的情况下,它显然已经绑定了…… - kirelagin
我也尝试使用抽象套接字,但是根据手册,这个长度始终为“2”,显然是错误的。 - kirelagin
我认为这可能是正常的。就像socketpair()返回的套接字一样,这些类型的套接字没有绑定到任何路径,但它们仍然连接到另一端的套接字。 - Celada
@kirelagin 感谢您的评论和努力! - Kevin S
1
@Celada 我不会感到惊讶,如果你是正确的,我希望有人能明确地告诉我是或否。谢谢! - Kevin S
2个回答

4

我真的不确定这是否是一个答案。也许更像是对一些研究的思考,但仍然值得一读。

accept(2)返回的值在Linux 3.16.0、NetBSD 6.1.4和Darwin 13.1.0内核中似乎与协议无关。实际上,这意味着accept(2)的第二个参数struct sockaddr * 只填充了所有协议之间共享的内容。因此,在成功的accept(2)之后,你手头拥有的数据远非完整的struct sockaddr_un

可能当初实现accept(2)时没有人认为这很重要,现在我们陷入了困境。幸运的是,如果你已经丢失了使用bind(2)调用套接字的路径名,并且现在想找回它,还有一种方法可以解决这个问题。

通过使用struct sockaddr_storage getsockname(2)函数,可以访问成员变量sun_path。因此,在成功调用accept(2)之后,可以通过调用getsockname(2)(这应该放在你的server.c的第40行之后)来确保你获得了所有有用的细节:

       struct sockaddr_storage ss;
       socklen_t sslen = sizeof(struct sockaddr_storage);
       if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
               struct sockaddr_un *un = (struct sockaddr_un *)&ss;
               printf("socket name is: %s\n", un->sun_path);
       }

或者只需使用以下代码:

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

#define NAME "socket"

int main(int argc, char **argv)
{
    int sock, msgsock, rval;
    struct sockaddr_un server, client;
    char buf[1024];

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("opening stream socket");
        exit(1);
    }

    server.sun_family = AF_UNIX;
    strcpy(server.sun_path, NAME);

    if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
        perror("binding stream socket");
        exit(1);
    }

    printf("Socket has name %s\n", server.sun_path);
    listen(sock, 5);

    for (;;) {
        socklen_t len = sizeof(client);
        msgsock = accept(sock, (struct sockaddr *)&client, &len);

        if (msgsock == -1)
            perror("accept");
        else do {
            printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));

            struct sockaddr_storage ss;
            socklen_t sslen = sizeof(struct sockaddr_storage);
            if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
                    struct sockaddr_un *un = (struct sockaddr_un *)&ss;
                    printf("socket name is: %s\n", un->sun_path);
            }

            bzero(buf, sizeof(buf));
            if ((rval = read(msgsock, buf, 1024)) < 0)
                perror("reading stream message");
            else if (rval == 0)
                printf("Ending connection\n");
            else
                printf("-->%s\n", buf);
        } while (rval > 0);

        close(msgsock);
    }
    close(sock);
    unlink(NAME);

    return 0;
}

这已经经过测试,也就是编译并产生了预期的结果,在运行内核为3.16.0的GNU/Linux系统、运行内核为6.1.4的NetBSD系统和运行内核为13.1.0的OS/X Mavericks系统上都能正常工作。在所有这些操作系统中,accept(2)的行为是一致的:结构中没有找到sun_pathgetsockname(2)在不同的操作环境中的行为也是一致的,从而使所有协议特定的细节都可用。


1

您还没有将客户端套接字绑定到地址。

对于connect()的工作,您不需要将客户端套接字绑定到地址; 但是,如果您希望在服务器中访问客户端地址,则必须进行绑定()。

有意义吗?

只需在客户端连接之前设置一个调用bind()。确保您为客户端使用的路径有效,并像往常一样检查错误。


谢谢!https://github.com/CarloWood/evio/commit/bfe006a6d33e2832c80b9971d53c01f5f50173cd - Carlo Wood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接