te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>c - Splicing from stdin to a socket: Broken pipe - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Splicing from stdin to a socket: Broken pipe - Stack Overflow

programmeradmin3浏览0评论

With the following, the client has a small chance of receiving "Broken pipe" when it attempts to splice, despite the socket being, as far as I can see, open. (The "small chance" seems to depends on various factors, including but not limited to system load.)

I'm not sure why this is the case. If I remember correctly, EPIPE is given when writing to a file descriptor with no listeners. It's even more confusing how it happens intermittently.

A script to reproduce how I build and run the programs, is attached.

server.c

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

#define SOCKET_PATH "test.sock"

int main(void)
{
    int server_fd, client_fd;
    struct sockaddr_un addr;

    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

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

    if (bind
        (server_fd, (struct sockaddr *)&addr,
         sizeof(struct sockaddr_un)) == -1) {
        perror("bind");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 1) == -1) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Listening %s\n", SOCKET_PATH);

    while (1) {
        client_fd = accept(server_fd, NULL, NULL);
        if (client_fd == -1) {
            perror("accept");
            close(server_fd);
            exit(EXIT_FAILURE);
        }

        close(client_fd);
    }

    close(server_fd);
    return 0;
}

client.c

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

#define SOCKET_PATH "test.sock"

int main(void)
{
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        perror("signal");
        return EXIT_FAILURE;
    }

    struct stat stdin_stat;
    if (fstat(STDIN_FILENO, &stdin_stat) == -1) {
        perror("fstat on stdin");
        return EXIT_FAILURE;
    }
    if (!S_ISFIFO(stdin_stat.st_mode)) {
        dprintf(STDERR_FILENO, "stdin must be a pipe\n");
        return EXIT_FAILURE;
    }
    int stdin_pipe_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ);
    if (stdin_pipe_size == -1) {
        perror("fcntl on stdin");
        return EXIT_FAILURE;
    }

    int sock;
    struct sockaddr_un addr;
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("internal socket creation");
        return EXIT_FAILURE;
    }
    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("internal socket connect");
        close(sock);
        return EXIT_FAILURE;
    }

    ssize_t stdin_bytes_spliced;
    while ((stdin_bytes_spliced =
        splice(STDIN_FILENO, NULL, sock, NULL, stdin_pipe_size,
               SPLICE_F_MORE)) > 0) {
    }
    if (stdin_bytes_spliced == -1) {
        perror("splice stdin to internal socket");
        close(sock);
        return EXIT_FAILURE;
    }

    close(sock);
    return EXIT_SUCCESS;
}

Running:

#!/bin/bash

rm -f test.sock

cc -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE server.c -o server
cc -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE client.c -o client

./server &
server_pid="$!"

sleep 1 # give it time to start listening

counter=0
while true
do
    echo "$counter"
    head -c 512 /dev/zero | ./client || break
    sleep 0.04
    counter=$((counter + 1))
done

kill $server_pid

(My "real problem" occurs in this Go code, but since sharing that would be difficult to reproduce, I decided to come up with a minimal working example in C, and verified that the erroneous behavior is the same across the MWE and the project's actual code.)

With the following, the client has a small chance of receiving "Broken pipe" when it attempts to splice, despite the socket being, as far as I can see, open. (The "small chance" seems to depends on various factors, including but not limited to system load.)

I'm not sure why this is the case. If I remember correctly, EPIPE is given when writing to a file descriptor with no listeners. It's even more confusing how it happens intermittently.

A script to reproduce how I build and run the programs, is attached.

server.c

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

#define SOCKET_PATH "test.sock"

int main(void)
{
    int server_fd, client_fd;
    struct sockaddr_un addr;

    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

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

    if (bind
        (server_fd, (struct sockaddr *)&addr,
         sizeof(struct sockaddr_un)) == -1) {
        perror("bind");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, 1) == -1) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("Listening %s\n", SOCKET_PATH);

    while (1) {
        client_fd = accept(server_fd, NULL, NULL);
        if (client_fd == -1) {
            perror("accept");
            close(server_fd);
            exit(EXIT_FAILURE);
        }

        close(client_fd);
    }

    close(server_fd);
    return 0;
}

client.c

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

#define SOCKET_PATH "test.sock"

int main(void)
{
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        perror("signal");
        return EXIT_FAILURE;
    }

    struct stat stdin_stat;
    if (fstat(STDIN_FILENO, &stdin_stat) == -1) {
        perror("fstat on stdin");
        return EXIT_FAILURE;
    }
    if (!S_ISFIFO(stdin_stat.st_mode)) {
        dprintf(STDERR_FILENO, "stdin must be a pipe\n");
        return EXIT_FAILURE;
    }
    int stdin_pipe_size = fcntl(STDIN_FILENO, F_GETPIPE_SZ);
    if (stdin_pipe_size == -1) {
        perror("fcntl on stdin");
        return EXIT_FAILURE;
    }

    int sock;
    struct sockaddr_un addr;
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("internal socket creation");
        return EXIT_FAILURE;
    }
    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("internal socket connect");
        close(sock);
        return EXIT_FAILURE;
    }

    ssize_t stdin_bytes_spliced;
    while ((stdin_bytes_spliced =
        splice(STDIN_FILENO, NULL, sock, NULL, stdin_pipe_size,
               SPLICE_F_MORE)) > 0) {
    }
    if (stdin_bytes_spliced == -1) {
        perror("splice stdin to internal socket");
        close(sock);
        return EXIT_FAILURE;
    }

    close(sock);
    return EXIT_SUCCESS;
}

Running:

#!/bin/bash

rm -f test.sock

cc -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE server.c -o server
cc -Wall -Wextra -Werror -pedantic -std=c99 -D_GNU_SOURCE client.c -o client

./server &
server_pid="$!"

sleep 1 # give it time to start listening

counter=0
while true
do
    echo "$counter"
    head -c 512 /dev/zero | ./client || break
    sleep 0.04
    counter=$((counter + 1))
done

kill $server_pid

(My "real problem" occurs in this Go code, but since sharing that would be difficult to reproduce, I decided to come up with a minimal working example in C, and verified that the erroneous behavior is the same across the MWE and the project's actual code.)

Share Improve this question edited Feb 18 at 0:08 Runxi Yu asked Feb 17 at 19:17 Runxi YuRunxi Yu 3432 silver badges9 bronze badges 6
  • 1 Your server closes socket connections immediately after accept()ing them, without waiting to receive anything from the client. That very likely explains why I consistently get an EPIPE from your example. Since you describe it as a "small chan[c]e" of an error, I presume that the example is not reflective of your real code, but it nevertheless does suggest a family of possible explanations for the EPIPEs you see in the real code. – John Bollinger Commented Feb 17 at 19:40
  • 1 EPIPE happens when writing to a filedescriptor which has been closed by the peer (but the local filedescriptor was not closed). In your specific case the server immediately closes the connection w/o reading any data, so this might be the reason. – Steffen Ullrich Commented Feb 17 at 19:49
  • 1 Did you leave out the server code that actually uses the accepted connection? That will almost certainly be relevant to the problem. – Barmar Commented Feb 17 at 19:51
  • The server code that I used in my actual project was written in Go (source, mirror). I think the problem is indeed that the server sometimes closes the connection before reading from the client. – Runxi Yu Commented Feb 18 at 0:02
  • @JohnBollinger With the example I provided, I was able to get EPIPE approximately one in ten times, and it seems to fluctuate depending on my system load (perhaps something along the lines of how fast the scheduler is working? not sure). The behavior of the small example I posted here matches that of my "real code", but I find C to be easier to work with for these operations, hence I posted my example in C – Runxi Yu Commented Feb 18 at 0:05
 |  Show 1 more comment

1 Answer 1

Reset to default 0

As the comments suggest, the problem is that the server prematurely closes the connection before reading anything out of it. (If it wasn't for the SIGPIPE handler, my program would terminate right away.)

My solution is to call shutdown(sock, SHUT_WR) on the client side after the splicing, so that the server side can read until EOF to obtain all of the standard input of the client. The server side shall only close the connection after it has done reading to EOF.

发布评论

评论列表(0)

  1. 暂无评论