使用AF_UNIX SOCK_DGRAM的“抽象套接字”

时间:2013-09-10 06:24:27

标签: c linux sockets abstract unix-socket

我正在尝试修改原始 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非常受欢迎

伊戈尔

P.S。由于我的声誉小于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);
}

2 个答案:

答案 0 :(得分:3)

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 abstract 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是你的“bind”或“sendto”的最后一个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。

答案 1 :(得分:2)

您的服务器绑定到客户端在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 /似乎我和其他答案有相同的复制和粘贴错误。