c - 将 'abstract sockets' 与 AF_UNIX SOCK_DGRAM 结合使用

标签 c linux sockets abstract unix-socket

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

不幸的是,我无法在我的客户端和服务器版本之间建立通信。当然,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

伊戈尔

附言由于我的声誉低于 10,我只能发布 2 个链接(请参阅本文顶部的 Kerrisk 的客户端 URL 和 Kerrrisk 的服务器 URL)。因此,我针对“如何使用抽象套接字”对 Kerrisk 的 C 代码片段进行了“剪切和粘贴”。它发表在他的书的第 1176 页, list 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”,表示无法访问服务器地址。


好的,再次阅读您的帖子后,我想我明白您的意思了。

当您使用非抽象 unix 套接字(sun_path[0] != 0) 时,服务器名称 len 是通过在内核中使用 strlen(sun_path) 计算的。所以最后一个参数并没有真正使用。

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

但是当你使用抽象的 unix 套接字(sun_path[0] == 0)时,服务器名称 len 是你“绑定(bind)”或“发送到”的最后一个参数。

net/unix/af_unix.c:unix_mkname:

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

所以当你把套接字绑定(bind)到有长度的地址时,

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)

内核认为服务器名称是一个数组,其长度为最后一个参数的长度。

因为,

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

内核不会认为地址相同,所以 sendto 返回 errno 111。

关于c - 将 'abstract sockets' 与 AF_UNIX SOCK_DGRAM 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18711996/

相关文章:

c99 转到过去的初始化

c - 如何在c中为字符串创建类型定义

node.js - 实时网络通知服务

javascript - NodeJS + Socket.IO : Problem accessing room variables

c - 为什么我在运行这段代码时会出现段错误?

c# - 通过 JNI 将字符串数组从 .c 文件传递​​到 java dll

c - Linux写整数

c++ - 成员: bug in GCC or my thinking?的初始化

c++ - 索引html的文本内容

java - ObjectInputStream - 如何等待新数据?