Linux终端通信程序FIFO写入无响应问题排查求助
问题排查与修复建议
你的程序无响应且功能失效,核心是架构设计缺陷+多处逻辑错误,下面逐一拆解问题:
1. 进程间全局变量完全不共享(致命问题)
server和client是独立的Linux进程,你定义的全局users数组在两个进程中是完全分开的内存空间——客户端往数组里加用户,服务器根本看不到;服务器的用户列表永远是空的,自然无法转发消息。
修复:必须让服务器统一管理用户列表,所有客户端的注册、消息请求都通过server_fifo发送给服务器处理,服务器再负责将消息转发到目标客户端的FIFO。
2. 客户端完全没处理终端输入(直接导致无响应)
client函数启动后,死循环读取自己的FIFO,但用户在终端输入的内容(比如TO alice hello)根本没被程序读取——程序没监听标准输入(stdin),所以你输入任何内容,程序都不会有反应。
修复:用select()或poll()同时监听stdin和用户FIFO,当stdin有输入时读取并解析命令:
// 客户端新增监听逻辑 fd_set read_fds; FD_ZERO(&read_fds); FD_SET(STDIN_FILENO, &read_fds); FD_SET(user_fifo, &read_fds); int max_fd = user_fifo > STDIN_FILENO ? user_fifo : STDIN_FILENO; while(1) { fd_set tmp_fds = read_fds; int ret = select(max_fd + 1, &tmp_fds, NULL, NULL, NULL); if(ret == -1) { perror("select"); exit(1); } // 处理终端输入 if(FD_ISSET(STDIN_FILENO, &tmp_fds)) { char input[MSG_SIZE]; fgets(input, sizeof(input), stdin); // 解析TO命令,发送给服务器 if(strstr(input, "TO ") == input) { char server_msg[MSG_SIZE + 64]; snprintf(server_msg, sizeof(server_msg), "MSG %s %s", name, input+3); int server_fd = open("server_fifo", O_WRONLY); if(server_fd != -1) { write(server_fd, server_msg, strlen(server_msg)); close(server_fd); } } } // 处理FIFO消息(接收服务器转发的消息) if(FD_ISSET(user_fifo, &tmp_fds)) { // 原有的读取FIFO逻辑 ... } }
3. 参数传递完全错误
main函数调用client(fd, sender_pid, name)时:
fd是未初始化的垃圾值,后续如果用这个fd写数据直接会出错sender_pid硬编码为-1,毫无意义
修复:client函数不需要这两个参数,直接在函数内部用getpid()获取自身PID,用STDIN_FILENO处理终端输入,修改函数定义为void client(char *name)。
4. FIFO逻辑混乱,通信链路断裂
- 客户端注册流程缺失:用户登录后,没有向
server_fifo发送注册请求,服务器永远不知道有新用户加入 - FIFO路径错误:客户端发送消息时,用
users[to_user].name作为FIFO路径打开,但服务器根本没保存用户的FIFO路径;而且客户端和服务器的users数组不共享,客户端根本拿不到目标用户的信息 - 未处理FIFO打开失败:服务器和客户端打开FIFO后,都没检查返回值是否为-1,比如服务器启动时如果
mkfifo失败(比如已存在),后续open会出错,但程序没处理
修复:
- 客户端登录时,立即向
server_fifo发送注册消息:
// client函数开头 int server_fd = open("server_fifo", O_WRONLY); if(server_fd == -1) { perror("connect to server"); exit(1); } char reg_msg[64]; snprintf(reg_msg, sizeof(reg_msg), "REGISTER %s %s", name, fifo_name); write(server_fd, reg_msg, strlen(reg_msg)); close(server_fd);
- 服务器接收注册消息后,将用户信息(name、fifo路径、fd)保存到自己的
users数组:
// server函数中解析消息 if(strstr(buffer, "REGISTER ") == buffer) { char *name = strtok(buffer+9, " "); char *fifo = strtok(NULL, " "); if(name && fifo) { int fifo_fd = open(fifo, O_WRONLY | O_NONBLOCK); if(fifo_fd != -1) { strncpy(users[num_users].name, name, sizeof(users[num_users].name)-1); users[num_users].name[sizeof(users[num_users].name)-1] = '\0'; strncpy(users[num_users].fifo, fifo, sizeof(users[num_users].fifo)-1); users[num_users].fifo[sizeof(users[num_users].fifo)-1] = '\0'; users[num_users].fd = fifo_fd; num_users++; } } }
- 客户端发送消息时,统一发送给服务器,由服务器转发到目标用户的FIFO,而不是客户端直接操作目标FIFO。
5. 字符串缓冲区溢出
struct user的name字段是20字节,但客户端注册时用strncpy(u.name, tok, 31),超过了字段长度,会导致缓冲区溢出,破坏进程内存,引发未知错误。
修复:
strncpy(u.name, tok, sizeof(u.name)-1); u.name[sizeof(u.name)-1] = '\0';
6. 其他细节问题
- 服务器启动时,
mkfifo如果遇到已存在的FIFO会报错,应该先检查是否存在,或者用mkfifo(..., 0666) || errno != EEXIST忽略已存在的情况 - 客户端的
UNREGISTER逻辑完全错误,sender_pid是-1,根本找不到用户,而且消息拼接用了未处理的msg变量,逻辑混乱 - 所有read/write操作都应该检查返回值,处理错误情况
内容的提问来源于stack exchange,提问作者Matthew
相关产品推荐
相关产品推荐

