6

我正在使用 Unix 域套接字进行一些测试,并且可以毫无问题地通过它们进行通信,但是,当我accept()在测试程序的服务器端调用时,返回的struct sockaddr_un不包含sun_path.

我很确定 Inet 套接字在调用后正确填写了它们的地址和端口accept(),所以我在测试程序中做错了什么还是我期待错误的结果?

我正在运行 CentOS 6.2 和 gcc 4.4.6。

示例代码:

服务器.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;
}

客户端.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;
}

只是重申这个问题:

为什么在服务器上调用后不sun_path填写accept()

4

2 回答 2

4

我真的不确定这是否是一个答案。可能它更像是对一些研究的沉思,尽管也许仍然值得一读。

填充的值accept(2)似乎与协议无关,至少在 Linux 3.16.0、NetBSD 6.1.4 和 Darwin 13.1.0 内核中。实际上,这意味着 , 的第二个参数accept(2)struct sockaddr *填充到所有协议之间共享的内容。所以成功后你手中的东西acccept(2)远不是一个完整的struct sockaddr_un

可能没有人认为在第一次实现时它会很重要,accept(2)现在我们被困住了。幸运的是,有一种方法可以解决这个问题,以防万一丢失了用于调用的套接字的路径名bind(2),并且现在想再次找到它。

和成员 sun_path 是可访问的struct sockaddr_storagegetsockname(2)因此,为确保您获得所有详细信息,请getsockname(2)在成功调用后调用accept(2)(这将放在您的第 40 行之后server.c):

       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 系统和配备 OS/X Mavericks 的系统上运行 13.1.0 内核。在所有这些行为中accept(2)是一致的:sun_path在结构中无处可寻。的行为getsockname(2)在不同的操作环境之间也是一致的,从而使所有协议特定的细节都可用。

于 2014-09-29T07:02:22.640 回答
1

您尚未将客户端套接字绑定到地址。

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

说得通?

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

于 2017-03-02T16:46:48.100 回答