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

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

spipe.c

 1 int cli_conn(const char *name)
 3     int fd, len, err, rval; 
 4     struct sockaddr_un un; 
 6     if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) { 
 7         printf ("create socket failed\n"); 
 8         return -1; 
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     }
23     printf ("connect to server ok\n"); 
24     return fd;
25 errout:
26     close (fd); 
27     errno = err; 
28     return rval;
29 }
32 int serv_listen (const char *name)
33 {
34     int fd, len, err, rval; 
35     struct sockaddr_un un; 
37     if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
38         printf ("socket failed\n"); 
39         return -1; 
40     }
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); 
49     if (bind (fd, (struct sockaddr *)&un, len) < 0) {
50         err = errno; 
51         printf ("bind failed\n");  
52         rval = -2; 
53         goto errout; 
54     }
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     }
64     printf ("start listen on socket ok\n"); 
65     return fd; 
66 errout:
67     close (fd); 
68     errno = err; 
69     return rval; 
70 }
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; 
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     }
85     len -= offsetof (struct sockaddr_un, sun_path); 
86     un.sun_path[len] = 0; 
87     printf ("accept %s ok\n", un.sun_path); 
89     unlink (un.sun_path); 
90     return clifd; 
92 errout:
93     close (clifd); 
94     errno = err; 
95     return rval; 

出错的位置在 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[])
 3     int c = 0; 
 4     log_open ("open.serv", LOG_PID, LOG_USER); 
 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     }
17     if (debug == 0)
18     {
19         log_to_stderr = 0; 
20         daemonize ("opend"); 
21     }
23     loop (); 
24     return 0; 

不使用 -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\n"); 
5         rval = -3; 
6         goto errout; 

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

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

暂时存疑……

前段时间写一个传递文件句柄的小 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 ...
在项目中使用postgresql数据库时要求在windows和linux双平台兼容。于是在windows下使用的接口在linux下爆出异常: psql:connections on Unix domain socket "/tmp/.s.PGSQL.5432" 经过分析和查看文档解决了这个问题: 1.原因:这是由于在linux下连接pg时使用了默认的unix domain ...
刚接触Android Studio写程序,写一个网络请求数据,创建的时候贪心,选了最新的Android P 即Android 10.0 ,API 29 。 但是忘记添加网络权限了,运行后看日志出现权限问题,根据提示添加网络权限。 于是加入两个权限 uses-permission android:name=“android.permission.ACCESS_NETWORK_STATE” uses-...
如何解决docker报错“Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemo
psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"? 出现该问题的很多,以下是目前碰到的几种情况,之后碰到...
SQLSTATE[HY000] [2002] Failed to create unix socket 框架thinkphp5,公司测试环境报该错误 原因:同事使用小皮时,把Apache切换到nginx时了,把 .htaccess文件清空了 解决如下:设置.htaccess文件 <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews RewriteEngine On RewriteCond %{REQUEST_FIL
最近使用unix domain socket来ipc。使用udp操作。上网一搜,嘿嘿。有两个外国的雷锋。把服务端和客户端代码都贴出来了。于是快乐地山寨了。编译、运行,结果挂啦。printf定位,发现是服务器调用sendto函数时总是失败。为什么?于是回头看那两个帖子,原来不是雷锋贴,是求助帖,问为啥不能正常工作。而且还是没人理睬的那种。汗啊。      试了几种体位,求索过程就忽略了。直接给出结
★"Unix Domain Sockets? - 我以前听说过"★ 经常被忽略的Unix domain socket机制是现代Unix最强特性之一。大多数Unix套接字编 程书籍只是从学术上讨论这个话题,甚至 ping命令在运行中采用了ICMP协议,需要发送ICMP报文。 但是只有root用户才能建立ICMP报文。而正常情况下,ping命令的权限应为 -rwsr-xr-x,即带有suid的文件,一旦该权限被修改,则普通用户无法正常使用该命令。 方案1:(一次性) sudo ping 192.168.1.100