3

我正在尝试修改原始Michael Kerrisk 的AF_UNIX SOCK_DGRAM 示例客户端/服务器程序。请单击以下两个链接(客户端服务器)以获取更多信息。示例代码最初发表在他的书The Linux Programming Interface, 第 57 章中。我的目标是根据Kerrisk 的代码示例,用“抽象套接字”定义替换“文件套接字”定义。

不幸的是,我无法在我的客户端和服务器版本之间建立通信。当然,Kerrisk 的版本非常出色

对我来说很明显,我在做一些根本错误的事情......但是......我不知道它是什么。

=================== 服务器============================== =

这是我的 Kerrisk 'server'代码版本:

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。更改大小计算以匹配客户端和服务器工作后。

编辑s/abstract_client/abstract_server/ 似乎我有与其他答案相同的复制和粘贴错误。

于 2013-09-10T07:22:58.697 回答