You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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连接关闭后的信号触发行为:

  1. 当客户端进程被终止,操作系统会自动关闭客户端持有的socket描述符,向服务端发送TCP的FIN包,告知连接已关闭。
  2. 服务端收到FIN包后,TCP连接进入半关闭状态,此时服务端的read()调用会返回0(表示对方已关闭读通道),但代码没有处理这个返回值,继续执行send()。
  3. 此时连接的写通道已经被客户端关闭,send()操作会触发SIGPIPE信号——这是Unix/Linux系统中,当向一个已关闭的连接写数据时默认触发的信号,该信号的默认处理动作就是终止进程,这就是服务端退出的直接原因。

补充说明

将accept()放入while循环能解决问题,是因为这种模式下服务端可以在一个连接断开后,继续等待并接受新的连接。但要彻底避免当前问题,还需要在代码中处理read()和send()的返回值:

  • 检测read()返回0时,主动关闭当前连接并跳出循环;
  • 要么忽略SIGPIPE信号,要么捕获该信号并自定义处理逻辑,避免进程被终止。

内容的提问来源于stack exchange,提问作者Matthew Tarry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 14:02:54