簡體   English   中英

在AF_UNIX SOCK_DGRAM中使用“抽象套接字”

[英]Using 'abstract sockets' with AF_UNIX SOCK_DGRAM

我正在嘗試修改原始的Michael Kerrisk的 AF_UNIX SOCK_DGRAM示例客戶端/服務器程序。 請單擊以下兩個鏈接( clientserver )以獲取更多信息。 該示例代碼最初發表在他的《 Linux編程接口》第57章中 我的目標是根據Kerrisk的代碼示例,用“抽象套接字”定義替換“文件套接字”定義。

不幸的是,我無法在客戶端和服務器的版本之間建立通信。 當然, Kerrisk的版本具有出色的色彩

對我來說很明顯,我做的事情根本上是錯誤的……但是……我不知道這是什么。

================== SERVER =============================== =

這是我對“服務器”的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);
}
if (sendto(sfd, argv[j], msgLen, 0, (struct sockaddr *) &svaddr,
            sizeof(struct sockaddr_un)) != msgLen)

最后一個arg無效,sockaddr_un地址長度應為

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

順便說一句,您應該打印出“ errno”值以精確定位從glibc API錯誤返回時發生的情況。 在上述情況下,“ errno”值為“ 111”,表示無法訪問服務器地址。


好,再次閱讀您的文章后,我想我明白了您的意思。

當您使用no抽象unix套接字(sun_path [0]!= 0)時,服務器名稱len是通過在內核中使用strlen(sun_path)來計算的。 因此,最后一個arg並未真正使用。

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

但是,當您使用抽象的unix套接字(sun_path [0] == 0)時,服務器名稱len是您的“綁定”或“發送到”的最后一個arg。

凈/ UNIX / af_unix.c:unix_mkname:

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

因此,當您將套接字綁定到具有長度的地址時,

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

內核認為服務器名稱是具有該長度的數組。

當您嘗試使用以下方式發送到地址時

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

內核認為服務器名稱是具有最后一個arg長度的數組。

因為,

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

內核不會認為地址相同,因此sendto返回errno 111。

您的服務器綁定到的地址與客戶端在sendto()使用的地址不同。 這是因為您在客戶端和服務器中計算svaddr的長度不同。

服務器: sizeof(sa_family_t) + strlen(abstract_server) + 1)

客戶端: sizeof(struct sockaddr_un)

Unix套接字地址是struct sockaddr_un.sun_path描述的路徑的整個給定長度。 更改大小計算后,以匹配客戶端和服務器工作。

編輯 s / abstract_client / abstract_server /似乎我有與其他答案相同的復制和粘貼錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM