Socket问题:终止客户端进程为何会导致服务端进程退出
Socket编程中客户端终止导致服务端退出的原因解析
问题背景
学习Socket编程时,在客户端和服务端的数据收发逻辑外都添加了while(1)循环,发现终止客户端进程后,服务端进程也随之退出。客户端和服务端拥有独立PID,理论上应该互不影响,即便连接失效,服务端也应该卡在循环里尝试读写。想知道是什么机制导致了这种行为,已知将accept()放入while循环可解决,但仍有疑问。
参考代码
Server.c
int server_fd, new_socket, valread; struct sockaddr_in address; int addrlen = sizeof(address); int opt = 1; char buffer[1024] = { 0 }; char *hello = "Hello from server"; if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) { perror("setsockopt"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); if (bind(server_fd, (struct sockaddr*)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } if (listen(server_fd, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } if ((new_socket = accept(server_fd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } while (1) { valread = read(new_socket, buffer, sizeof(buffer)); printf("%s\n", buffer); send(new_socket, hello, strlen(hello), 0); } close(new_socket); shutdown(server_fd, SHUT_RDWR); return 0; }
Client.c
int client_fd, status, valread; struct sockaddr_in serv_addr; char *hello = "Hello from client"; char buffer[1024] = { 0 }; if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed"); exit(EXIT_FAILURE); } serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) { perror("Address not supported"); exit(EXIT_FAILURE); } if ((status = connect(client_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) { perror("Connection failed"); } while (1) { send(client_fd, hello, strlen(hello), 0); printf("Hello message sent\n"); valread = read(client_fd, buffer, sizeof(buffer)); printf("%s\n", buffer); } close(client_fd); return 0; }
原因分析
这并非IPC机制直接导致,而是TCP连接关闭后的信号触发行为:
- 当客户端进程被终止,操作系统会自动关闭客户端持有的socket描述符,向服务端发送TCP的FIN包,告知连接已关闭。
- 服务端收到FIN包后,TCP连接进入半关闭状态,此时服务端的
read()调用会返回0(表示对方已关闭读通道),但代码没有处理这个返回值,继续执行send()。 - 此时连接的写通道已经被客户端关闭,
send()操作会触发SIGPIPE信号——这是Unix/Linux系统中,当向一个已关闭的连接写数据时默认触发的信号,该信号的默认处理动作就是终止进程,这就是服务端退出的直接原因。
补充说明
将accept()放入while循环能解决问题,是因为这种模式下服务端可以在一个连接断开后,继续等待并接受新的连接。但要彻底避免当前问题,还需要在代码中处理read()和send()的返回值:
- 检测
read()返回0时,主动关闭当前连接并跳出循环; - 要么忽略SIGPIPE信号,要么捕获该信号并自定义处理逻辑,避免进程被终止。
内容的提问来源于stack exchange,提问作者Matthew Tarry
相关产品推荐
相关产品推荐

