[PATCH] Add simple splice-fromnet example
[splice.git] / splice-fromnet.c
1 /*
2  * Splice from network to pipe. Currently splicing from a socket is not
3  * supported, so this test case demonstrates how to use read + vmsplice/splice
4  * to achieve the desired effect. This still involves a copy, so it's
5  * not as fast as real splice from socket would be.
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <netdb.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <string.h>
17 #include <sys/time.h>
18 #include <errno.h>
19
20 #include "splice.h"
21
22 #define PSIZE   4096
23 #define MASK    (PSIZE - 1)
24
25 #define ALIGN(buf)      (void *) (((unsigned long) (buf) + MASK) & ~MASK)
26
27 static int splice_flags;
28
29 static int usage(char *name)
30 {
31         fprintf(stderr, "%s: [-g] port\n", name);
32         return 1;
33 }
34
35 static unsigned long mtime_since(struct timeval *s, struct timeval *e)
36 {
37         double sec, usec;
38
39         sec = e->tv_sec - s->tv_sec;
40         usec = e->tv_usec - s->tv_usec;
41         if (sec > 0 && usec < 0) {
42                 sec--;
43                 usec += 1000000;
44         }
45
46         sec *= (double) 1000;
47         usec /= (double) 1000;
48
49         return sec + usec;
50 }
51
52 static unsigned long mtime_since_now(struct timeval *s)
53 {
54         struct timeval t;
55
56         gettimeofday(&t, NULL);
57         return mtime_since(s, &t);
58 }
59
60 static int recv_loop(int fd, char *buf)
61 {
62         unsigned long kb_recv = 0, spent;
63         struct timeval s;
64         struct iovec iov;
65         int idx = 0;
66
67         gettimeofday(&s, NULL);
68
69         do {
70                 char *ptr = buf + idx * SPLICE_SIZE;
71                 int ret;
72
73                 ret = recv(fd, ptr, SPLICE_SIZE, MSG_WAITALL);
74                 if (ret < 0) {
75                         perror("recv");
76                         return 1;
77                 } else if (ret != SPLICE_SIZE)
78                         break;
79
80                 iov.iov_base = ptr;
81                 iov.iov_len = SPLICE_SIZE;
82                 ret = vmsplice(STDOUT_FILENO, &iov, 1, splice_flags);
83                 if (ret < 0) {
84                         perror("vmsplice");
85                         return 1;
86                 } else if (ret != SPLICE_SIZE) {
87                         fprintf(stderr, "bad vmsplice %d\n", ret);
88                         return 1;
89                 }
90                 idx = (idx + 1) & 0x01;
91                 kb_recv += (SPLICE_SIZE / 1024);
92         } while (1);
93
94         spent = mtime_since_now(&s);
95         fprintf(stderr, "%lu MiB/sec (%luKiB in %lu msec)\n", kb_recv / spent, kb_recv, spent);
96
97         return 0;
98 }
99
100 static int parse_options(int argc, char *argv[])
101 {
102         int c, index = 1;
103
104         while ((c = getopt(argc, argv, "g")) != -1) {
105                 switch (c) {
106                 case 'g':
107                         splice_flags = SPLICE_F_GIFT;
108                         index++;
109                         break;
110                 default:
111                         return -1;
112                 }
113         }
114
115         return index;
116 }
117
118 int main(int argc, char *argv[])
119 {
120         struct sockaddr_in addr;
121         unsigned short port;
122         unsigned int len;
123         int fd, opt, sk, index;
124         char *buf;
125
126         if (check_output_pipe())
127                 return usage(argv[0]);
128
129         index = parse_options(argc, argv);
130         if (index == -1 || index + 1 > argc)
131                 return usage(argv[0]);
132
133         port = atoi(argv[index]);
134
135         fd = socket(PF_INET, SOCK_STREAM, 0);
136         if (fd < 0)
137                 return error("socket");
138
139         opt = 1;
140         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
141                 return error("setsockopt");
142
143         memset(&addr, 0, sizeof(addr));
144         addr.sin_addr.s_addr = htonl(INADDR_ANY);
145         addr.sin_port = htons(port);
146
147         if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
148                 return error("bind");
149
150         if (listen(fd, 1) < 0)
151                 return error("listen");
152
153         len = sizeof(addr);
154         sk = accept(fd, &addr, &len);
155         if (sk < 0)
156                 return error("accept");
157
158         fprintf(stderr, "Connected\n");
159
160         buf = ALIGN(malloc(2 * SPLICE_SIZE - 1));
161
162         return recv_loop(sk, buf);
163
164         close(fd);
165         return 0;
166 }