Commit | Line | Data |
---|---|---|
beeecb44 JA |
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 | } |