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

C语言客户端在scanf/fgets等待输入时无法响应SIGINT信号的解决求助

客户端SIGINT信号处理问题解决方案

问题描述

用C语言开发客户端-服务器程序,在Ubuntu终端运行时遇到以下问题:

  • 客户端调用fgets等待用户输入时,按下CTRL+C触发SIGINT信号
  • 客户端未按预期终止(正常应向服务器发送终止信号并关闭连接)
  • 终端显示^C,后续输入仅在屏幕显示,按回车后无法发送给服务器,连接陷入阻塞

曾尝试用scanf配合EINTR_ABORT宏、带EINTR_ABORT的fgets,均未解决问题。相关代码如下:

scanf代码

while ((scanf("%s", buff)) == -1) EINTR_ABORT("Errore di scanf");
getchar();

fgets代码

if(fgets(buff_ID, sizeof(buff_ID), stdin)== NULL)
   EINTR_ABORT("Errore di fgets");

EINTR_ABORT宏定义

#define ISTANT_ABORT(warning) ({ 
    fprintf(stderr, "%s: %s.\n", warning, strerror(errno)); 
    exit(EXIT_FAILURE);
})

#define EINTR_ABORT(warning) ({ 
    if(errno != EINTR) 
        ISTANT_ABORT(warning); 
})

解决方案

核心问题是默认SIGINT处理仅打断阻塞系统调用(如fgets/scanf),但你的EINTR_ABORT宏仅忽略EINTR错误,未执行终止清理逻辑。需自定义SIGINT信号处理函数,触发信号时完成资源清理与进程退出。

实现步骤

  1. 注册自定义SIGINT信号处理函数,替换默认行为
  2. 在信号处理函数中执行异步信号安全的操作:关闭客户端socket、向服务器发送终止标识、退出进程
  3. 调整输入循环逻辑,遇到EINTR错误时直接退出循环

完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <errno.h>

int client_socket; // 全局socket,供信号处理函数访问

// SIGINT信号处理函数(仅调用异步信号安全的函数)
void sigint_handler(int sig) {
    // 向服务器发送终止标识(按需定义格式)
    const char* terminate_msg = "CLIENT_EXIT";
    write(client_socket, terminate_msg, strlen(terminate_msg));
    // 关闭socket
    close(client_socket);
    // 用_exit直接退出,避免触发stdio缓冲区刷新(非信号安全)
    _exit(EXIT_FAILURE);
}

int main() {
    // 此处添加客户端socket初始化、连接服务器的代码...
    // 示例:client_socket = socket(...); connect(...);

    // 注册SIGINT信号处理
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = sigint_handler;
    // 屏蔽其他信号,避免处理SIGINT时被打断
    sigfillset(&sa.sa_mask);
    sigaction(SIGINT, &sa, NULL);

    char buff_ID[256];
    while (1) {
        printf("请输入内容:");
        fflush(stdout); // 强制刷新输出缓冲区,确保提示符及时显示

        if (fgets(buff_ID, sizeof(buff_ID), stdin) == NULL) {
            // 若为EINTR,说明被SIGINT打断,退出循环
            if (errno == EINTR) {
                break;
            }
            perror("fgets 读取失败");
            close(client_socket);
            exit(EXIT_FAILURE);
        }

        // 去除换行符(可选)
        buff_ID[strcspn(buff_ID, "\n")] = '\0';
        // 发送数据到服务器
        if (write(client_socket, buff_ID, strlen(buff_ID)) == -1) {
            perror("发送数据失败");
            close(client_socket);
            exit(EXIT_FAILURE);
        }
    }

    // 正常退出时清理资源
    close(client_socket);
    return 0;
}

关键说明

  • 原宏失效原因:EINTR_ABORT遇到EINTR错误时直接跳过,未触发终止逻辑,导致终端状态异常,后续输入失效。
  • 使用sigaction而非signal:signal函数行为在不同系统不一致,sigaction是POSIX标准,更可靠。
  • 信号处理函数限制:仅调用异步信号安全函数(如write、close、_exit),避免使用printf、fprintf等可能引发死锁的stdio函数。
  • 刷新输出缓冲区:fflush(stdout)确保输入提示符及时显示,避免行缓冲导致用户看不到输入提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:16:08