/*
- * Splice from network to pipe. Currently splicing from a socket is not
- * supported, so this test case demonstrates how to use read + vmsplice/splice
- * to achieve the desired effect. This still involves a copy, so it's
- * not as fast as real splice from socket would be.
+ * Splice from network to stdout
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
+#include <sys/poll.h>
#include "splice.h"
-#define PSIZE 4096
-#define MASK (PSIZE - 1)
-
-#define ALIGN(buf) (void *) (((unsigned long) (buf) + MASK) & ~MASK)
-
-static int splice_flags;
+static unsigned int splice_size = SPLICE_SIZE;
+static unsigned int splice_flags;
+static int wait_for_poll;
static int usage(char *name)
{
- fprintf(stderr, "%s: [-g] port\n", name);
+ fprintf(stderr, "%s: [-s splice size] [-w wait for poll] [-n non-blocking] port\n", name);
return 1;
}
-static unsigned long mtime_since(struct timeval *s, struct timeval *e)
+static int splice_from_net(int fd)
{
- double sec, usec;
+ while (1) {
+ int ret;
- sec = e->tv_sec - s->tv_sec;
- usec = e->tv_usec - s->tv_usec;
- if (sec > 0 && usec < 0) {
- sec--;
- usec += 1000000;
- }
+ if (wait_for_poll) {
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
- sec *= (double) 1000;
- usec /= (double) 1000;
+ ret = poll(&pfd, 1, -1);
+ if (ret < 0)
+ return error("poll");
+ else if (!ret)
+ continue;
- return sec + usec;
-}
+ if (!(pfd.revents & POLLIN))
+ continue;
+ }
-static unsigned long mtime_since_now(struct timeval *s)
-{
- struct timeval t;
+ ret = ssplice(fd, NULL, STDOUT_FILENO, NULL, splice_size, 0);
+
+ if (ret < 0)
+ return error("splice");
+ else if (!ret)
+ break;
+ }
- gettimeofday(&t, NULL);
- return mtime_since(s, &t);
+ return 0;
}
-static int recv_loop(int fd, char *buf)
+static int get_connect(int fd, struct sockaddr_in *addr)
{
- unsigned long kb_recv = 0, spent;
- struct timeval s;
- struct iovec iov;
- int idx = 0;
-
- gettimeofday(&s, NULL);
+ socklen_t socklen = sizeof(*addr);
+ int ret, connfd;
do {
- char *ptr = buf + idx * SPLICE_SIZE;
- int ret;
-
- ret = recv(fd, ptr, SPLICE_SIZE, MSG_WAITALL);
- if (ret < 0) {
- perror("recv");
- return 1;
- } else if (ret != SPLICE_SIZE)
- break;
-
- iov.iov_base = ptr;
- iov.iov_len = SPLICE_SIZE;
- ret = vmsplice(STDOUT_FILENO, &iov, 1, splice_flags);
- if (ret < 0) {
- perror("vmsplice");
- return 1;
- } else if (ret != SPLICE_SIZE) {
- fprintf(stderr, "bad vmsplice %d\n", ret);
- return 1;
- }
- idx = (idx + 1) & 0x01;
- kb_recv += (SPLICE_SIZE / 1024);
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ ret = poll(&pfd, 1, -1);
+ if (ret < 0)
+ return error("poll");
+ else if (!ret)
+ continue;
+
+ connfd = accept(fd, (struct sockaddr *) addr, &socklen);
+ if (connfd < 0)
+ return error("accept");
+ break;
} while (1);
-
- spent = mtime_since_now(&s);
- fprintf(stderr, "%lu MiB/sec (%luKiB in %lu msec)\n", kb_recv / spent, kb_recv, spent);
-
- return 0;
+
+ return connfd;
}
static int parse_options(int argc, char *argv[])
{
int c, index = 1;
- while ((c = getopt(argc, argv, "g")) != -1) {
+ while ((c = getopt(argc, argv, "s:w:n")) != -1) {
switch (c) {
- case 'g':
- splice_flags = SPLICE_F_GIFT;
+ case 's':
+ splice_size = atoi(optarg);
+ index++;
+ break;
+ case 'w':
+ wait_for_poll = atoi(optarg);
+ index++;
+ break;
+ case 'n':
+ splice_flags |= SPLICE_F_NONBLOCK;
index++;
break;
default:
return index;
}
+
int main(int argc, char *argv[])
{
struct sockaddr_in addr;
unsigned short port;
- unsigned int len;
- int fd, opt, sk, index;
- char *buf;
+ int connfd, opt, fd, index;
+
+ if (argc < 2)
+ return usage(argv[0]);
if (check_output_pipe())
return usage(argv[0]);
port = atoi(argv[index]);
- fd = socket(PF_INET, SOCK_STREAM, 0);
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0)
return error("socket");
return error("setsockopt");
memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
return error("bind");
-
if (listen(fd, 1) < 0)
return error("listen");
- len = sizeof(addr);
- sk = accept(fd, &addr, &len);
- if (sk < 0)
- return error("accept");
+ connfd = get_connect(fd, &addr);
+ if (connfd < 0)
+ return connfd;
- fprintf(stderr, "Connected\n");
-
- buf = ALIGN(malloc(2 * SPLICE_SIZE - 1));
-
- return recv_loop(sk, buf);
-
- close(fd);
- return 0;
+ return splice_from_net(connfd);
}