/* * This program demonstrates how a splice() from a TCP socket blocks, * even though the destination pipe is empty. * * Copyright 2009 Content Management AG, Cologne, Germany * Author: Max Kellermann * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2 of the * License. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include static size_t fill_socket(int fd, size_t max_fill) { static char buffer[4096]; ssize_t nbytes; size_t sent = 0; do { nbytes = send(fd, buffer, sizeof(buffer), MSG_DONTWAIT); if (nbytes >= 0) sent += (size_t)nbytes; else if (errno == EAGAIN) /* the socket buffer is full */ break; else { perror("send() failed"); exit(1); } } while (sent < max_fill); printf("sent %zu bytes\n", sent); return sent; } static void run(size_t max_fill, size_t max_splice, unsigned splice_flags, bool early_close) { int i, listen_socket, client_socket, server_socket, pipefds[2]; struct sockaddr_in sa; size_t sent; ssize_t nbytes; /* set up socket and pipe */ memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(1234); listen_socket = socket(AF_INET, SOCK_STREAM, 0); i = 1; if (listen_socket < 0 || setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) || bind(listen_socket, (const struct sockaddr *)&sa, sizeof(sa)) < 0 || listen(listen_socket, 1) < 0) { perror("failed to listen"); exit(1); } client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0 || connect(client_socket, (const struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("failed to connect"); exit(1); } server_socket = accept(listen_socket, NULL, 0); if (server_socket < 0) { perror("failed to accept"); exit(1); } close(listen_socket); if (pipe(pipefds) < 0) { perror("pipe() failed"); exit(1); } /* fill the socket buffer */ sent = fill_socket(client_socket, max_fill); printf("%sclosing client socket\n", early_close ? "" : "not "); if (early_close) close(client_socket); /* now splice from the socket into the pipe, as much as possible */ if (max_splice == 0) max_splice = sent; printf("invoking splice(%zu, 0x%x)\n", max_splice, splice_flags); nbytes = splice(server_socket, NULL, pipefds[1], NULL, max_splice, splice_flags); if (nbytes >= 0) printf("splice(%zu, 0x%x) = %zi\n", max_splice, splice_flags, nbytes); if (nbytes < 0) printf("splice(%zu, 0x%x) failed: %s\n", max_splice, splice_flags, strerror(errno)); if (!early_close) close(client_socket); close(server_socket); close(pipefds[0]); close(pipefds[1]); } int main(int argc, char **argv) { (void)argc; (void)argv; run(4096, 0, 0, true); run(262144, 4096, 0, true); run(262144, 0, SPLICE_F_NONBLOCK, true); /* when closing the client socket, this does not block! */ run(262144, 0, 0, true); run(4096, 0, 0, false); run(262144, 4096, 0, false); run(262144, 0, SPLICE_F_NONBLOCK, false); /* this will block (2.6.31, 2.6.32-rc6) */ run(262144, 0, 0, false); return 0; }