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

C Socket服务器向所有客户端发消息时出现SIGPIPE错误求助

解决C Socket服务器SIGPIPE错误问题

SIGPIPE错误本质是向已断开的TCP连接发送数据,系统默认会触发该信号终止进程。你的代码中有两处send存在触发风险,下面逐一分析并给出修复方案:

一、两处send的问题点

1. 广播消息的循环send

这段循环向其他客户端转发消息时,没有过滤无效的socket描述符:

for ( int client= 0 ; client < max_clients ; client++)
{
    sd_test = client_socket[client];
    if (sd_test != sd_new)
    {
        send(sd_test , newString.c_str() , strlen(newString.c_str()) , 0 );
    }
}
  • 问题:client_socket[client]可能是0(未被占用的位置),或者对应的客户端已断开但未被服务器清理,此时调用send会触发SIGPIPE。

2. 循环外的独立send

代码末尾的send(sd_new , last_send.c_str() , strlen(last_send.c_str()) , 0 );

  • 问题:sd_new仅在收到以:开头的消息时才会赋值,初次运行或未收到这类消息时,sd_new是未初始化的垃圾值;即使初始化过,对应的客户端也可能已断开,触发SIGPIPE。

二、具体修复步骤

1. 忽略SIGPIPE信号(基础防护)

在main函数开头添加代码,让系统忽略SIGPIPE信号,此时send遇到断开连接时会返回-1而非终止进程:

signal(SIGPIPE, SIG_IGN);

2. 修复广播循环的send逻辑

添加socket有效性检查,并处理发送失败的情况:

for ( int client= 0 ; client < max_clients ; client++)
{
    sd_test = client_socket[client];
    // 只向有效且非当前发送者的socket发送
    if (sd_test > 0 && sd_test != sd_new)
    {
        ssize_t ret = send(sd_test, newString.c_str(), newString.length(), 0);
        if (ret == -1) {
            // 若为连接断开类错误,清理该socket
            if (errno == EPIPE || errno == ECONNRESET) {
                getpeername(sd_test, (struct sockaddr*)&address, (socklen_t*)&addrlen);
                printf("Client disconnected (send failed), ip %s , port %d \n",
                       inet_ntoa(address.sin_addr), ntohs(address.sin_port));
                close(sd_test);
                client_socket[client] = 0;
            }
        }
    }
}

3. 修复循环外的send问题

这段send的逻辑模糊,建议根据需求调整:

  • 如果是广播收集的last_send消息,改为遍历所有有效客户端发送:
if (!last_send.empty()) {
    for (int client = 0; client < max_clients; client++) {
        sd_test = client_socket[client];
        if (sd_test > 0) {
            ssize_t ret = send(sd_test, last_send.c_str(), last_send.length(), 0);
            if (ret == -1 && (errno == EPIPE || errno == ECONNRESET)) {
                getpeername(sd_test, (struct sockaddr*)&address, (socklen_t*)&addrlen);
                printf("Client disconnected (send failed), ip %s , port %d \n",
                       inet_ntoa(address.sin_addr), ntohs(address.sin_port));
                close(sd_test);
                client_socket[client] = 0;
            }
        }
    }
    last_send.clear(); // 发送后清空缓存
}
  • 如果不需要这段逻辑,直接删除即可。

4. 初始化未赋值变量

在main开头初始化sd_new为无效值,避免未定义行为:

int master_socket , addrlen , new_socket , client_socket[30] ,
    max_clients = 30 , activity, i , valread , sd, sd_new = -1;

三、额外优化建议

  • 定期清理client_socket数组中的无效描述符,避免残留失效的socket;
  • 处理send的部分发送情况(返回值小于发送长度时,需循环发送剩余数据);
  • 用FD_ISSET检测socket时,先判断sd > 0,避免无效描述符干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:13:17