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 域套接字还有什么区别么?

    暂时存疑……

  • 相关阅读:
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 连号区间数
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Java实现 蓝桥杯 历届试题 大臣的旅费
    Navicat查询哪些表有指定字段名
  • 原文地址:https://www.cnblogs.com/goodcitizen/p/does_unix_domain_socket_failed_due_to_daemon.html
Copyright © 2011-2022 走看看