zoukankan      html  css  js  c++  java
  • [apue] 作为 daemon 启动, Unix Domain Socket 侦听失败?

    前段时间写一个传递文件句柄的小 demo,有 server 端、有 client 端,之间通过 Unix Domain Socket 通讯。

    在普通模式下,双方可以正常建立连接,当server端作为daemon启动时,则第一次启动成功,之后再启动, listen 会连接报 ENOTSUPP 错误,导致启动失败。

    spipe.c

     1 int cli_conn(const char *name)
     2 {
     3     int fd, len, err, rval; 
     4     struct sockaddr_un un; 
     5 
     6     if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) { 
     7         printf ("create socket failed
    "); 
     8         return -1; 
     9     }
    10 
    11     printf ("create socket ok
    "); 
    12     memset (&un, 0, sizeof (un)); 
    13     un.sun_family = AF_UNIX; 
    14     strcpy (un.sun_path, name); 
    15     len = offsetof (struct sockaddr_un, sun_path) + strlen (name); 
    16     if (connect (fd, (struct sockaddr *)&un, len) < 0) {
    17         err = errno; 
    18         printf ("connect failed
    "); 
    19         rval = -4; 
    20         goto errout; 
    21     }
    22 
    23     printf ("connect to server ok
    "); 
    24     return fd;
    25 errout:
    26     close (fd); 
    27     errno = err; 
    28     return rval;
    29 }
    30 
    31 
    32 int serv_listen (const char *name)
    33 {
    34     int fd, len, err, rval; 
    35     struct sockaddr_un un; 
    36 
    37     if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
    38         printf ("socket failed
    "); 
    39         return -1; 
    40     }
    41 
    42     printf ("create socket ok
    "); 
    43     unlink (name); 
    44     memset (&un, 0, sizeof(un)); 
    45     un.sun_family = AF_UNIX; 
    46     strcpy (un.sun_path, name); 
    47     len = offsetof (struct sockaddr_un, sun_path) + strlen (name); 
    48 
    49     if (bind (fd, (struct sockaddr *)&un, len) < 0) {
    50         err = errno; 
    51         printf ("bind failed
    ");  
    52         rval = -2; 
    53         goto errout; 
    54     }
    55 
    56     printf ("bind socket to path ok
    "); 
    57     if (listen (fd, QLEN) < 0) { 
    58         err = errno; 
    59         printf ("listen failed, errno %d
    ", errno); 
    60         rval = -3; 
    61         goto errout; 
    62     }
    63 
    64     printf ("start listen on socket ok
    "); 
    65     return fd; 
    66 errout:
    67     close (fd); 
    68     errno = err; 
    69     return rval; 
    70 }
    71 
    72 int serv_accept (int listenfd, uid_t *uidptr)
    73 {
    74     int clifd, err, rval; 
    75     time_t staletime; 
    76     struct sockaddr_un un; 
    77     struct stat statbuf; 
    78 
    79     size_t len = sizeof (un); 
    80     if ((clifd = accept (listenfd, (struct sockaddr *)&un, &len)) < 0) { 
    81         printf ("accept failed
    "); 
    82         return -1; 
    83     }
    84 
    85     len -= offsetof (struct sockaddr_un, sun_path); 
    86     un.sun_path[len] = 0; 
    87     printf ("accept %s ok
    ", un.sun_path); 
    88 
    89     unlink (un.sun_path); 
    90     return clifd; 
    91 
    92 errout:
    93     close (clifd); 
    94     errno = err; 
    95     return rval; 
    96 }

    出错的位置在 serv_listen (line 57) 处,出错时的 server 端输出为:

    Jan 17 00:24:44 localhost opend: create socket ok
    Jan 17 00:24:44 localhost opend: bind socket to path ok
    Jan 17 00:24:44 localhost opend: listen failed, errno 95
    Jan 17 00:24:44 localhost opend: serv_listen error: Operation not supported
    

    errno 95 为 ENOTSUPP。不以 daemon 运行时正常的输出如下:

    create socket ok
    bind socket to path ok
    start listen on socket ok
    accept  ok
    new connection: uid 0, fd 4
    

    可能细心的读者会觉得,以 daemon 方式运行 printf 怎么还可以输出呢,是有以下宏定义做了处理:

    1 #ifdef USE_APUE
    2 #include "../apue.h"
    3 #define printf log_msg
    4 #endif

    以 daemon 运行时会定义 USE_APUE 宏,从而将 printf 重定义为 log_msg 输出到 syslog。

    下面是 server 端的代码:

    csopend2.c

     1 int main (int argc, char *argv[])
     2 {
     3     int c = 0; 
     4     log_open ("open.serv", LOG_PID, LOG_USER); 
     5 
     6     opterr = 0; // don't want getopt() writting to stderr !
     7     while ((c = getopt (argc, argv, "d")) != EOF) {
     8         switch (c) { 
     9             case 'd':
    10                 debug = log_to_stderr = 1; 
    11                 break; 
    12             case '?':
    13                 err_quit ("unrecongnized option: -%c", optopt); 
    14         }
    15     }
    16 
    17     if (debug == 0)
    18     {
    19         log_to_stderr = 0; 
    20         daemonize ("opend"); 
    21     }
    22 
    23     loop (); 
    24     return 0; 
    25 }

    不使用 -d 时表示 daemon 运行(与常识相反?),上面标黄的代码就是。

    对应的 client 端代码:

    csopenc.c

    一开始怀疑是用于 listen 的本地 socket 文件已经存在,于是去 /tmp 目录看了下,果然有 opend 这个文件,删除之,再运行,不行;

    然后怀疑是没有复用端口(?)所致,于是在 listen 之前添加了以下代码段:

    1     int opt = 1; 
    2     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt)) < 0) {
    3         err = errno; 
    4         printf ("setsockopt failed
    "); 
    5         rval = -3; 
    6         goto errout; 
    7     }

    设置端口复用。编译、运行,输出如下:

    Jan 17 00:43:11 localhost opend: create socket ok
    Jan 17 00:43:11 localhost opend: bind socket to path ok
    Jan 17 00:43:11 localhost opend: set socket option ok
    Jan 17 00:43:11 localhost opend: listen failed, errno 95
    Jan 17 00:43:11 localhost opend: serv_listen error: Operation not supported
    

    设置成功了,但还是不行

    难道 daemon 与普通进程使用 Unix 域套接字还有什么区别么?

    暂时存疑……

  • 相关阅读:
    SDOI2008 Sandy的卡片
    BZOJ2555 Substring
    CTSC2012 熟悉的文章
    递增
    丢失的牛
    【模板】点分治
    陌上花开(三维偏序)
    Holes(河鼠入洞)
    弹飞河鼠
    树状数组1
  • 原文地址:https://www.cnblogs.com/goodcitizen/p/does_unix_domain_socket_failed_due_to_daemon.html
Copyright © 2011-2022 走看看