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

UNIX STREAM Socket服务器重复接收相同消息问题求助

UNIX STREAM Socket 重复接收相同消息问题排查与解决

问题现象

  • Linux环境下,UNIX STREAM Socket客户端仅发送1次消息(消息为每次全新生成,无静态变量复用导致重复的情况),但服务器在接收第一条消息后,持续打印相同内容
  • 客户端基于libev处理套接字,服务器使用select模型

排查过程

  1. 系统调用追踪:通过strace分析发现,服务器周期性触发读事件,每次读取到相同消息;但客户端仅执行过1次write操作,排除客户端重复发送的可能
  2. 代码逻辑校验:确认客户端消息生成逻辑无静态变量复用,排除代码层面的重复发送问题
  3. 函数调用溯源:最终定位到发送消息的send_event函数被编译进libabc.so共享库,另有一个守护进程通过调用该库的包装函数,重复触发了send_event,导致服务器持续收到重复消息

解决方案

通过PID校验限制仅目标客户端进程触发消息发送:

  • 客户端启动时创建.pid文件,写入自身进程PID
  • 在send_event函数中,读取.pid文件中的目标PID,校验当前进程PID与目标PID是否一致,仅当匹配时才执行消息发送操作

核心代码片段

客户端核心代码(libev处理)

#include <ev.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

static void send_message(struct ev_loop *loop, ev_io *w, int revents) {
    char msg[64];
    // 生成随机消息(确保无静态变量复用)
    snprintf(msg, sizeof(msg), "test_msg_%ld", random());
    write(w->fd, msg, strlen(msg));
    // 仅发送一次,停止监听写事件
    ev_io_stop(loop, w);
}

int main() {
    // 写入客户端PID到文件
    int fd = open("/tmp/client.pid", O_CREAT|O_WRONLY|O_TRUNC, 0644);
    if (fd != -1) {
        char pid_str[16];
        snprintf(pid_str, sizeof(pid_str), "%d", getpid());
        write(fd, pid_str, strlen(pid_str));
        close(fd);
    }

    struct ev_loop *loop = EV_DEFAULT;
    ev_io write_watcher;
    int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {0};
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/tmp/test_socket");
    connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));

    ev_io_init(&write_watcher, send_message, sock_fd, EV_WRITE);
    ev_io_start(loop, &write_watcher);
    ev_run(loop, 0);
    close(sock_fd);
    return 0;
}

服务器核心代码(select模型)

#include <sys/select.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main() {
    unlink("/tmp/test_socket");
    int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr = {0};
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path, "/tmp/test_socket");
    bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(sock_fd, 5);

    fd_set read_fds;
    char buf[64];
    while(1) {
        FD_ZERO(&read_fds);
        FD_SET(sock_fd, &read_fds);
        select(sock_fd+1, &read_fds, NULL, NULL, NULL);
        if(FD_ISSET(sock_fd, &read_fds)) {
            int conn_fd = accept(sock_fd, NULL, NULL);
            ssize_t read_len;
            while((read_len = read(conn_fd, buf, sizeof(buf)-1)) > 0) {
                buf[read_len] = '\0';
                printf("Received: %s\n", buf);
                memset(buf, 0, sizeof(buf));
            }
            close(conn_fd);
        }
    }
    close(sock_fd);
    return 0;
}

strace关键日志(服务器侧)

# 服务器重复触发读事件并读取相同内容
select(4, [3], NULL, NULL, NULL)        = 1 (in [3])
accept(3, {sa_family=AF_UNIX, sun_path="/tmp/test_socket"}, [110]) = 4
read(4, "test_msg_12345", 64)           = 12
write(1, "Received: test_msg_12345\n", 25) = 25
read(4, "test_msg_12345", 64)           = 12
write(1, "Received: test_msg_12345\n", 25) = 25
# 持续重复上述read和write操作

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:17:53