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

使用splice(2)将标准输入转发至UNIX域套接字时遇无效参数错误

问题

我需要将标准输入的所有内容复制到UNIX域套接字,想用splice(2)实现,但运行代码后报错splice: Invalid argument。以下是我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <fcntl.h>

int main(void) {
    int sock;
    struct sockaddr_un addr;
    const char *socket_path = "/tmp/test.sock";

    int exit_status = EXIT_SUCCESS;

    if (socket_path == NULL) {
        exit(EXIT_FAILURE);
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("socket");
        exit_status = EXIT_FAILURE;
        goto cleanup;
    }

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);

    if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
        perror("connect");
        exit_status = EXIT_FAILURE;
        goto cleanup;
    }

    ssize_t bytes_spliced;
    /* I'm setting the offsets to NULL because they don't make sense in pipes or sockets */
    while ((bytes_spliced = splice(STDIN_FILENO, NULL, sock, NULL, 16384, SPLICE_F_MORE)) > 0) {
    }

    if (bytes_spliced == -1) {
        perror("splice");
        exit_status = EXIT_FAILURE;
    }

cleanup:
    close(sock);
    return exit_status;
}
解决方案

splice(2)的核心限制是:至少有一个文件描述符必须是管道。直接将标准输入和UNIX套接字传给splice不符合要求,因此返回EINVAL错误。

解决方法是通过管道中转数据,分两步完成splice操作:

  1. 将标准输入的数据转移到管道写端
  2. 将管道读端的数据转移到UNIX套接字

修改后的代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <fcntl.h>

int main(void) {
    int sock;
    struct sockaddr_un addr;
    const char *socket_path = "/tmp/test.sock";
    int pipe_fds[2];
    int exit_status = EXIT_SUCCESS;

    if (socket_path == NULL) {
        exit(EXIT_FAILURE);
    }

    // 创建中转管道
    if (pipe(pipe_fds) == -1) {
        perror("pipe");
        exit_status = EXIT_FAILURE;
        goto cleanup;
    }

    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("socket");
        exit_status = EXIT_FAILURE;
        goto cleanup_pipe;
    }

    memset(&addr, 0, sizeof(struct sockaddr_un));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);

    if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) {
        perror("connect");
        exit_status = EXIT_FAILURE;
        goto cleanup_sock;
    }

    ssize_t bytes_spliced;
    while (1) {
        // 从标准输入splice到管道
        bytes_spliced = splice(STDIN_FILENO, NULL, pipe_fds[1], NULL, 16384, SPLICE_F_MORE);
        if (bytes_spliced <= 0) {
            break;
        }

        // 从管道splice到套接字
        bytes_spliced = splice(pipe_fds[0], NULL, sock, NULL, 16384, SPLICE_F_MORE);
        if (bytes_spliced == -1) {
            perror("splice to socket");
            exit_status = EXIT_FAILURE;
            break;
        }
    }

    if (bytes_spliced == -1) {
        perror("splice from stdin");
        exit_status = EXIT_FAILURE;
    }

cleanup_sock:
    close(sock);
cleanup_pipe:
    close(pipe_fds[0]);
    close(pipe_fds[1]);
cleanup:
    return exit_status;
}

关键细节

  • 管道是splice合法操作的必要条件,它作为数据中转的临时载体
  • SPLICE_F_MORE标志告知内核后续还有数据要发送,可优化TCP数据包的合并,减少系统调用开销
  • 所有打开的文件描述符都要正确关闭,避免资源泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 10:10:03