6

Unix ドメイン ソケットでいくつかのテストを行っており、問題なく通信できaccept()ますが、テスト プログラムのサーバー側で呼び出すと、返さstruct sockaddr_unれた にsun_path.

呼び出し後に Inet ソケットのアドレスとポートが適切に入力されていることは確かですaccept()が、テスト プログラムで何か間違ったことを行っているのでしょうか、それとも間違った結果を期待しているのでしょうか?

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

質問を繰り返すだけです:

サーバーでsun_pathの呼び出し後に入力されないのはなぜですか?accept()

4

2 に答える 2

4

これが答えであるかどうかは本当にわかりません。おそらく、それは何らかの研究についての黙想のようなものですが、それでも読む価値があるかもしれません.

によって満たされる値accept(2)は、少なくとも Linux 3.16.0、NetBSD 6.1.4、および Darwin 13.1.0 カーネルでは、プロトコルにまったく依存していないようです。実際には、これは への 2 番目のパラメータが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 システム、およびカーネル 13.1.0 を実行する OS/X Mavericks を搭載したシステムで動作するように、コンパイルして期待される結果を生成します。のこれらの動作のすべてにおいて、accept(2)一貫性があります:sun_pathは、塗りつぶされた構造のどこにも見つかりません。の動作はgetsockname(2)異なる動作環境間でも一貫しており、すべてのプロトコル固有の詳細を利用できます。

于 2014-09-29T07:02:22.640 に答える