[apue]作为daemon启动,UnixDomainSocket侦听失败?
前段时间写一个传递文件句柄的小 demo,有 server 端、有 client 端,之间通过 Unix Domain Socket 通讯。
在普通模式下,双方可以正常建立连接,当server端作为daemon启动时,则第一次启动成功,之后再启动, listen 会连接报 ENOTSUPP 错误,导致启动失败。
spipe.c
1int cli_conn(constchar *name) 2{ 3int fd, len, err, rval; 4struct sockaddr_un un; 56if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
7 printf ("create socket failed
");
8return -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);
16if (connect (fd, (struct sockaddr *)&un, len) < 0) {
17 err = errno;
18 printf ("connect failed
");
19 rval = -4;
20goto errout;
21 }
22
23 printf ("connect to server ok
");
24return fd;
25errout:
26 close (fd);
27 errno = err;
28return rval;
29}
30
31
32int serv_listen (constchar *name)
33{
34int fd, len, err, rval;
35struct sockaddr_un un;
36
37if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
38 printf ("socket failed
");
39return -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
49if (bind (fd, (struct sockaddr *)&un, len) < 0) {
50 err = errno;
51 printf ("bind failed
");
52 rval = -2;
53goto errout;
54 }
55
56 printf ("bind socket to path ok
");
57if (listen (fd, QLEN) < 0) {
58 err = errno;
59 printf ("listen failed, errno %d
", errno);
60 rval = -3;
61goto errout;
62 }
63
64 printf ("start listen on socket ok
");
65return fd;
66errout:
67 close (fd);
68 errno = err;
69return rval;
70}
71
72int serv_accept (int listenfd, uid_t *uidptr)
73{
74int clifd, err, rval;
75 time_t staletime;
76struct sockaddr_un un;
77struct stat statbuf;
78
79 size_t len = sizeof (un);
80if ((clifd = accept (listenfd, (struct sockaddr *)&un, &len)) < 0) {
81 printf ("accept failed
");
82return -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);
90return clifd;
91
92errout:
93 close (clifd);
94 errno = err;
95return rval;
96 }
出错的位置在 serv_listen (line 57) 处,出错时的 server 端输出为:
Jan 17 00:24:44 localhost opend: create socket okJan 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 okbind socket to path ok
start listen on socket ok
accept ok
new connection: uid 0, fd 4
可能细心的读者会觉得,以 daemon 方式运行 printf 怎么还可以输出呢,是有以下宏定义做了处理:
1#ifdef USE_APUE2 #include "../apue.h"3#define printf log_msg
4#endif
以 daemon 运行时会定义 USE_APUE 宏,从而将 printf 重定义为 log_msg 输出到 syslog。
下面是 server 端的代码:
csopend2.c
1int main (int argc, char *argv[]) 2{ 3int c = 0; 4 log_open ("open.serv", LOG_PID, LOG_USER); 56 opterr = 0; // don"t want getopt() writting to stderr !
7while ((c = getopt (argc, argv, "d")) != EOF) {
8switch (c) {
9case"d":
10 debug = log_to_stderr = 1;
11break;
12case"?":
13 err_quit ("unrecongnized option: -%c", optopt);
14 }
15 }
16
17if (debug == 0)
18 {
19 log_to_stderr = 0;
20 daemonize ("opend");
21 }
22
23 loop ();
24return0;
25 }
不使用 -d 时表示 daemon 运行(与常识相反?),上面标黄的代码就是。
对应的 client 端代码:
csopenc.c
一开始怀疑是用于 listen 的本地 socket 文件已经存在,于是去 /tmp 目录看了下,果然有 opend 这个文件,删除之,再运行,不行;
然后怀疑是没有复用端口(?)所致,于是在 listen 之前添加了以下代码段:
1int opt = 1; 2if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (constvoid *)&opt, sizeof(opt)) < 0) {3 err = errno; 4 printf ("setsockopt failed"); 5 rval = -3; 6goto errout; 7 }
设置端口复用。编译、运行,输出如下:
Jan 17 00:43:11 localhost opend: create socket okJan 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 域套接字还有什么区别么?
暂时存疑……
以上是 [apue]作为daemon启动,UnixDomainSocket侦听失败? 的全部内容, 来源链接: utcz.com/z/512830.html