在学习Muduo网络库过程中,net/EventLoop.cc
中看到了对SIGPIPE
信号的处理,因此在此处记录下。
在TCP编程中,SIGPIPE
是一种信号,它通常发生在以下情况:
When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated. —from section 5.13 in “Unix Network Programming” by Richard Stevens.
也就是,socket在接收到了 RST
packet 后,程序仍然向这个 socket
写入数据,那么就会产生SIGPIPE信号。
这种情况在网络应用中经常发生,譬如说,当 client 连接到 server 之后,这时候 server 准备向 client 发送多条消息,但在发送消息之前,client 进程意外奔溃了,那么接下来 server 在发送多条消息的过程中,就会出现SIGPIPE
信号。
处理SIGPIPE的常见方法是忽略它,以防止服务器进程意外终止。你可以在你的程序中使用以下代码来忽略SIGPIPE
信号, 然后根据错误errno
和自身需要针对性处理,增强程序的稳定性:1
2
::signal(SIGPIPE, SIG_IGN);
举个示例,复现SIGPIPE
信号产生:
Server1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void handle_client(int fd)
{
// 假设此时 client 奔溃, 那么 server 将接收到 client 发送的 FIN
sleep(5);
// 写入第一条消息
char msg1[MAXLINE] = {"first message"};
ssize_t n = write(fd, msg1, strlen(msg1));
printf("write %ld bytes\n", n);
// 此时第一条消息发送成功,server 接收到 client 发送的 RST
sleep(1);
// 写入第二条消息,出现 SIGPIPE 信号,导致 server 被杀死
char msg2[MAXLINE] = {"second message"};
n = write(fd, msg2, strlen(msg2));
printf("%ld, %s\n", n, strerror(errno));
}
int main()
{
unsigned short port = 8888;
struct sockaddr_in server_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
int listenfd = socket(AF_INET , SOCK_STREAM , 0);
bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(listenfd, 128);
int fd = accept(listenfd, NULL, NULL);
handle_client(fd);
return 0;
}
Client
使用nc作为client连接Server,然后终止,1
2
3
4
5
6
7(base) gcc -o server server.c
(base) ./server
[1] 20340
(base) nc localhost 8888
^C
(base) write 13 bytes
[1] + 20340 broken pipe ./server
- client 连接到 server,client中止,发送一个
FIN
给 server - server 发送第一条消息给 client。但 client 已经退出了,所以 client 的 TCP 协议栈会发送一个
RST
给 server - server 在接收到
RST
之后,继续写入第二条消息。往一个已经收到RST
的 socket 继续写入数据,将导致SIGPIPE
信号,从而终止 server进程