我有一个父进程(客户端)通过 Unix 域套接字(也称为 IPC 套接字)与子进程(服务器)进行通信。
套接字是使用socketpair()
数据报类型创建的。
我用来posix_spawn()
启动子进程,以及close()
父子进程中我不需要的套接字对的末尾。
在孩子身上我使用poll()
和recv()
。
这一切都很好。
现在,我希望当客户端关闭套接字末端或客户端终止时通知子级(服务器)。
我本来希望在 上得到一个POLLHUP
或POLLERR
事件close()
,但我什么也没得到。
当使用lsof -U
列出我的 2 个进程打开的 Unix 域套接字时,我看到套接字的另一端是none
在客户端被杀死之后。
如果这很重要的话,这是在 macOS 上。
我缺少什么?当客户端关闭 Unix 域套接字时如何收到通知?
答案1
我根据您的描述整理了一个示例应用程序,但有一些细微的差别。首先,我使用SOCK_STREAM
而不是SOCK_DGRAM
.这种变化给了我你正在寻找的行为——POLLHUP
意味着套接字不再连接;SOCK_DGRAM
不是面向连接的套接字。您有需要使用的理由吗SOCK_DGRAM
?
此示例与您所描述的第二个小区别是我使用fork()
而不是posix_spawn()
.这不应该影响您感兴趣的行为;对我来说,用 编写独立的示例更容易fork()
。
#include <poll.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
int main(void)
{
int sockets[2] = {};
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
perror("socketpair");
return 1;
}
const pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 2;
}
if (pid > 0) {
/* parent */
close(sockets[0]);
sockets[0] = 0;
for (int i = 0; i < 3; ++i) {
write(sockets[1], "hello", sizeof("hello"));
sleep(2);
}
/* sockets[1] will get closed on parent termination */
return 0;
}
/* child */
close(sockets[1]);
sockets[1] = 0;
struct pollfd fds = {
.fd = sockets[0],
.events = POLLIN | POLLHUP,
};
while (poll(&fds, 1, 10 * 1000) > 0) {
if (fds.revents & POLLHUP) {
printf("--- Received hangup\n");
break;
}
if (fds.revents & POLLERR) {
printf("!!! Received error\n");
break;
}
if (fds.revents & POLLIN) {
char buffer[32] = {};
if (recv(sockets[0], buffer, sizeof buffer, 0) < 0) {
perror("recv");
return 3;
}
printf("--> Received message '%s'\n", buffer);
}
}
/* sockets[0] will get closed on child termination */
return 0;
}
在这个程序中,父母给孩子写消息;子进程接收这些消息并处理它们(在这里,只是将它们打印到标准输出)。父进程发送 3 条消息,然后终止。
当我运行这个程序时,我看到了我认为您正在寻找的行为:
$ ./a.out
--> Received message 'hello'
--> Received message 'hello'
--> Received message 'hello'
--- Received hangup
如果您必须使用,SOCK_DGRAM
那么对的调用poll()
最终将超时(在我的示例中,我有 10 秒的超时)。您可以关闭该事件的客户端进程。