3

元の Michael Kerrisk のAF_UNIX SOCK_DGRAM サンプル クライアント/サーバー プログラムを変更しようとしています。詳細については、次の 2 つのリンク ( clientserver ) をクリックしてください。サンプル コードは、彼の著書The Linux Programming Interface の第 57 章で最初に公開されました。私の目標は、 Kerrisk のコード例に基づいて、「ファイル ソケット」定義を「抽象ソケット」定義に置き換えることです。

残念ながら、私のバージョンのクライアントとサーバーの間で通信を確立できません。もちろん、Kerrisk のバージョンは飛行色で動作します

私が根本的に間違ったことをしていることは明らかです...しかし...私はそれが何であるかを理解できませんでした。

=================== サーバー ============================= =

「サーバー」のケリスクのコードの私のバージョンは次のとおりです。

int main(int argc, char *argv[])
{
   struct sockaddr_un svaddr, claddr;
   int sfd, j;
   ssize_t numBytes;
   socklen_t len;
   char buf[BUF_SIZE];

   char *abstract_server;

   sfd = socket(AF_UNIX, SOCK_DGRAM, 0);       /* Create server socket */
   if (sfd == -1)
       errExit("socket");

   abstract_server = "viper_server";
   /* Construct well-known address and bind server socket to it */

   if (remove(abstract_server) == -1 && errno != ENOENT)
       errExit("remove-%s", abstract_server);  

   memset(&svaddr, 0, sizeof(struct sockaddr_un));
   svaddr.sun_family = AF_UNIX;
   strncpy(&svaddr.sun_path[1], abstract_server, strlen(abstract_server));

   if (bind(sfd, (struct sockaddr *) &svaddr, 
     sizeof(sa_family_t) + strlen(abstract_server) + 1) == -1)
       errExit("bind");

   /* Receive messages, convert to uppercase, and return to client */

   for (;;) {
       len = sizeof(struct sockaddr_un);
       numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
                           (struct sockaddr *) &claddr, &len);
       if (numBytes == -1)
           errExit("recvfrom");

       printf("Server received %ld bytes from %s\n", (long) numBytes,
               claddr.sun_path);

       for (j = 0; j < numBytes; j++)
       buf[j] = toupper((unsigned char) buf[j]);

       if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) != numBytes)
           fatal("sendto");
     }
 }

========================= クライアント ======================= ====

これは、 「クライアント」のKerrisk のコードの私のバージョンです。

int main(int argc, char *argv[])
{
    struct sockaddr_un svaddr, claddr;
    int sfd, j;
    size_t msgLen;
    ssize_t numBytes;
    char resp[BUF_SIZE];

    char *abstract_client;
    char *abstract_server;

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s msg...\n", argv[0]);

    /* Create client socket; bind to unique pathname (based on PID) */

    sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sfd == -1)
        errExit("socket");

    abstract_client = "viper_client";
    abstract_server = "viper_server";

    memset(&claddr, 0, sizeof(struct sockaddr_un));
    claddr.sun_family = AF_UNIX;
    strncpy(&claddr.sun_path[1], abstract_client, strlen(abstract_client)); 

    if (bind(sfd, (struct sockaddr *) &claddr, 
      sizeof(sa_family_t) + strlen(abstract_client) + 1) == -1)
        errExit("bind");

    /* Construct address of server */

    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(&svaddr.sun_path[1], abstract_server, strlen(abstract_server));

    /* Send messages to server; echo responses on stdout */

    for (j = 1; j < argc; j++) {
        msgLen = strlen(argv[j]);       /* May be longer than BUF_SIZE */

       /* code FIX */
       if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
                 (sizeof(sa_family_t) + strlen(abstract_server) + 1) ) != msgLen) 
              fatal("sendto");

        /* original - non working code - replaced with the code FIX above 
        if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
                sizeof(struct sockaddr_un)) != msgLen)
        {
            fatal("sendto");
        } */

        numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, NULL, NULL);
        /* Or equivalently: numBytes = recv(sfd, resp, BUF_SIZE, 0);
                        or: numBytes = read(sfd, resp, BUF_SIZE); */
        if (numBytes == -1)
            errExit("recvfrom");
        printf("Response %d: %.*s\n", j, (int) numBytes, resp);
    }

    remove(claddr.sun_path);            /* Remove client socket pathname */
    exit(EXIT_SUCCESS);
}

テストの開始方法:

> ./server &
> ./client  aa bb cc
> result:  ERROR: sendto

任意のヘルプ | 提案 | ソリューション | RTFMは大歓迎です

イゴール

PS 私の評判は 10 未満なので、2 つのリンクしか投稿できません (この投稿の上部にある Kerrisk のクライアント URL、Kerrrisk のサーバー URL を参照してください)。そこで、「抽象ソケットの使用方法」のKerrisk のC コード スニペットを「カット アンド ペースト」しました。これは彼の本の 1176 ページ、リスト 57-8 に掲載されています。

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_un addr;
    char *str;

    memset(&addr, 0, sizeof(struct sockaddr_un));  /* Clear address structure */
    addr.sun_family = AF_UNIX;                     /* UNIX domain address */

    /* addr.sun_path[0] has already been set to 0 by memset() */

    str = "xyz";        /* Abstract name is "\0abc" */
    strncpy(&addr.sun_path[1], str, strlen(str));

    // In early printings of the book, the above two lines were instead:
    //
    // strncpy(&addr.sun_path[1], "xyz", sizeof(addr.sun_path) - 2);
    //            /* Abstract name is "xyz" followed by null bytes */

    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sockfd == -1)
        errExit("socket");

    if (bind(sockfd, (struct sockaddr *) &addr,
            sizeof(sa_family_t) + strlen(str) + 1) == -1)
        errExit("bind");

    // In early printings of the book, the final part of the bind() call
    // above was instead:
    //        sizeof(struct sockaddr_un)) == -1)

    sleep(60);

    exit(EXIT_SUCCESS);
}
4

2 に答える 2

3
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
            sizeof(struct sockaddr_un)) != msgLen)

The last arg is invalid, the sockaddr_un address length should be

sizeof(sa_family_t) + strlen(abstract_client) + 1;

BTW, you should print out the "errno" value to precisely locate what happened when errorly returned from the glibc API. In the above case, the "errno" value is "111", indicating the server address cannot be reached.


OK, After having read your post once more, I think I got what you mean.

When you use the no abstract unix sockets(sun_path[0] != 0), the server name len is calculated by using strlen(sun_path) in the kernel. So the last arg is not really used.

net/unix/af_unix.c:unix_mkname

if (sunaddr->sun_path[0]) {
    /* ... */
    ((char *)sunaddr)[len] = 0;
    len = strlen(sunaddr->sun_path)+1+sizeof(short);
    return len;
}  

But when you use the abstract unix sockets(sun_path[0] == 0), the server name len is the last arg that your "bind" or "sendto".

net/unix/af_unix.c:unix_mkname:

*hashp = unix_hash_fold(csum_partial(sunaddr, len, 0));

So when you bind the socket to the address with length,

if (bind(sfd, (struct sockaddr *) &svaddr,
    sizeof(sa_family_t) + strlen(abstract_server) + 1) == -1)

Kernel think the server name is an array with that length.

When you try to send to an address with

if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
            sizeof((struct sockaddr)) != msgLen)

Kernel think the server name is an array with that length of the last arg.

Because,

sizeof((struct sockaddr)) != sizeof(sa_family_t) + strlen(abstract_server) + 1

Kernel will not think the address is the same, so sendto returns with errno 111.

于 2013-09-10T07:44:11.280 に答える
2

サーバーは、クライアントが で使用するアドレスとは異なるアドレスにバインドしますsendto()。これは、svaddr の長さの計算方法がクライアントとサーバーで異なるためです。

サーバ: sizeof(sa_family_t) + strlen(abstract_server) + 1)

クライアント: sizeof(struct sockaddr_un)

UNIX ソケット アドレスは、 で説明されているパスの所定の長さ全体struct sockaddr_un.sun_pathです。クライアントとサーバーの作業に合わせてサイズ計算を変更した後。

EDIT s/abstract_client/abstract_server/ 他の回答と同じコピー&ペースト エラーが発生したようです。

于 2013-09-10T07:22:58.697 に答える