[apue] 作为 daemon 启动, Unix Domain Socket 侦听失败?
程序员文章站
2022-03-25 17:41:38
前段时间写一个传递文件句柄的小 demo,有 server 端、有 client 端,之间通过 Unix Domain Socket 通讯。 在普通模式下,双方可以正常建立连接,当server端作为daemon启动时,则第一次启动成功,之后再启动, listen 会连接报 ENOTSUPP 错误,导 ......
前段时间写一个传递文件句柄的小 demo,有 server 端、有 client 端,之间通过 unix domain socket 通讯。
在普通模式下,双方可以正常建立连接,当server端作为daemon启动时,则第一次启动成功,之后再启动, listen 会连接报 enotsupp 错误,导致启动失败。
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\n"); 8 return -1; 9 } 10 11 printf ("create socket ok\n"); 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\n"); 19 rval = -4; 20 goto errout; 21 } 22 23 printf ("connect to server ok\n"); 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\n"); 39 return -1; 40 } 41 42 printf ("create socket ok\n"); 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\n"); 52 rval = -2; 53 goto errout; 54 } 55 56 printf ("bind socket to path ok\n"); 57 if (listen (fd, qlen) < 0) { 58 err = errno; 59 printf ("listen failed, errno %d\n", errno); 60 rval = -3; 61 goto errout; 62 } 63 64 printf ("start listen on socket ok\n"); 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\n"); 82 return -1; 83 } 84 85 len -= offsetof (struct sockaddr_un, sun_path); 86 un.sun_path[len] = 0; 87 printf ("accept %s ok\n", 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 端的代码:
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 端代码:
一开始怀疑是用于 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\n"); 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 域套接字还有什么区别么?
暂时存疑……