[apue] 做爲 daemon 啓動, Unix Domain Socket 偵聽失敗?

前段時間寫一個傳遞文件句柄的小 demo,有 server 端、有 client 端,之間經過 Unix Domain Socket 通信。git

在普通模式下,雙方能夠正常創建鏈接,當server端做爲daemon啓動時,則第一次啓動成功,以後再啓動, listen 會鏈接報 ENOTSUPP 錯誤,致使啓動失敗。github

spipe.cbash

 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 端輸出爲:socket

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 運行時正常的輸出以下:oop

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

 

可能細心的讀者會以爲,以 daemon 方式運行 printf 怎麼還能夠輸出呢,是有如下宏定義作了處理:ui

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

 

以 daemon 運行時會定義 USE_APUE 宏,從而將 printf 重定義爲 log_msg 輸出到 syslog。spa

下面是 server 端的代碼:debug

csopend2.ccode

 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 運行(與常識相反?),上面標黃的代碼就是。server

對應的 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\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 域套接字還有什麼區別麼?

暫時存疑……

相關文章
相關標籤/搜索