Ticket #1174: sendfile_test_loop.c

File sendfile_test_loop.c, 8.8 KB (added by pokotilenko@…, 10 years ago)

Direct epoll/sendfile test with signal interruption and sendfile loop

Line 
1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4#include <unistd.h>
5#include <errno.h>
6#include <fcntl.h>
7#include <sys/epoll.h>
8#include <sys/sendfile.h>
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <sys/stat.h>
12#include <netdb.h>
13#include <netinet/in.h>
14#include <netinet/tcp.h>
15#include <sys/time.h>
16#include <signal.h>
17
18int sending=0;
19
20/* ****************************************************************** */
21int create_listener(char *host, char *port) {
22
23 int fd;
24
25 struct addrinfo hints;
26 struct addrinfo *result, *rp;
27 int s;
28
29 memset(&hints, 0, sizeof(struct addrinfo));
30 hints.ai_family = AF_UNSPEC;
31 hints.ai_socktype = SOCK_STREAM;
32 hints.ai_flags = AI_PASSIVE;
33 hints.ai_protocol = 0;
34
35 if((s = getaddrinfo(host, port, &hints, &result))) {
36 printf("getaddrinfo: %s\n", gai_strerror(s));
37 return(-1);
38 }
39
40 for(rp=result; rp; rp=rp->ai_next) {
41 if(0>(fd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol))) {
42 printf("socket\n");
43 continue;
44 }
45
46 int yes=1;
47 if(0>setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) {
48 printf("setsockopt\n");
49 continue;
50 }
51
52 if(0>bind(fd, rp->ai_addr, rp->ai_addrlen)) {
53 printf("could not bind socket to address\n");
54 close(fd);
55 continue;
56 }
57 break;
58 }
59
60 if(!rp) { /* No address succeeded */
61 fprintf(stderr, "Could not connect\n");
62 return(-1);
63 }
64
65 freeaddrinfo(result); /* No longer needed */
66
67 /* set socket to listen for incoming connections */
68 /* allow a queue of 5 */
69 if(0>listen(fd, 5)) {
70 printf("listen error\n");
71 return(-1);
72 }
73
74 return(fd);
75}
76
77/* ****************************************************************** */
78int do_use_fd(int fd_in, off_t *offset, size_t *size_left, int fd_out) {
79
80 ssize_t n;
81
82 sending=1;
83 if(-1==(n=sendfile(fd_out, fd_in, offset, *size_left))) {
84 sending=0;
85 printf("Sendfile: failed: %s (%d)\n", strerror(errno), errno);
86 return(-1);
87 }
88 sending=0;
89 if(!n) {
90 printf("Sendfile: short send: %s (%d)\n", strerror(errno), errno);
91 return(-2);
92 }
93 printf("Sendfile: report: sent: %ld\n", n);
94 *size_left-=n;
95
96 return(!(*size_left));
97}
98
99/* ****************************************************************** */
100int do_use_fd_in_a_loop(int fd_in, off_t *offset, size_t *size_left, int fd_out) {
101
102 ssize_t n;
103
104 sending=1;
105 while(*size_left && 0<(n=sendfile(fd_out, fd_in, offset, *size_left))) {
106 *size_left-=n;
107 printf("Sendfile: report: sent: %ld, left: %ld\n", n, *size_left);
108 }
109 sending=0;
110
111 if(n<=0 && (errno==EAGAIN || errno==EINTR)) {
112 printf("Sendfile: sent: %ld, returning as: %s (%d)\n", n, strerror(errno), errno);
113 return(!(*size_left));
114 }
115 if(-1==n) {
116 printf("Sendfile: failed: %s (%d)\n", strerror(errno), errno);
117 return(-1);
118 }
119 if(!n) {
120 printf("Sendfile: short send: %s (%d)\n", strerror(errno), errno);
121 return(-2);
122 }
123
124 return(!(*size_left));
125}
126/* ****************************************************************** */
127void timer_handler(int signum) {
128
129 if(sending) {
130 printf("******************* Timer probably have happened while inside sendfile **********************\n");
131 }
132 //printf("Timer.\n");
133}
134
135/* ****************************************************************** */
136void setup_timer(long tv_sec, long tv_usec) {
137
138 struct itimerval new_value;
139
140 signal(SIGALRM, timer_handler);
141
142 new_value.it_interval.tv_sec=tv_sec;
143 new_value.it_interval.tv_usec=tv_usec;
144 new_value.it_value.tv_sec=tv_sec;
145 new_value.it_value.tv_usec=tv_usec;
146 setitimer(ITIMER_REAL, &new_value, NULL);
147}
148
149/* ****************************************************************** */
150int main(int argc, char **argv) {
151
152 char *host;
153 char *port;
154 char *fname;
155 int et=0;
156 long timer_usec;
157 int fd_in;
158 int n;
159 int r;
160
161 off_t offset;
162 size_t size_left;
163 struct stat st;
164
165 struct sockaddr_in local;
166 socklen_t addrlen;
167
168 int (*do_use_fd_fn)(int, off_t*, size_t*, int)=do_use_fd;
169
170 if(argc<5 || argc>6 || !(timer_usec=atol(argv[4])) || (argc==6 && strcmp(argv[5], "et") && strcmp(argv[5], "et_loop"))) {
171 printf("Usage: %s bind_addr bind_port file_name timer_usec [et|et_loop]\n", argv[0]);
172 printf("\ttimer_usec: set timer to timer_usec microseconds, program is signalled with SIGALRM each time timer expires\n");
173 printf("\tet: if set use edge triggered epoll mode\n");
174 printf("\tet_loop: if set use edge triggered epoll mode and sendfile in a loop intul EAGAIN\n");
175 return(1);
176 }
177
178 host=argv[1];
179 port=argv[2];
180 fname=argv[3];
181 if(argc==6) {
182 et=1;
183 if(!strcmp(argv[5], "et_loop")) do_use_fd_fn=do_use_fd_in_a_loop;
184 }
185
186 printf("Serving file: %s\n", fname);
187 printf("Timer microseconds: %lu\n", timer_usec);
188 printf("Edge triggered mode: %s\n", et?"set":"not set");
189
190#define MAX_EVENTS 10
191 struct epoll_event ev, events[MAX_EVENTS];
192 int listen_sock, conn_sock, nfds, epollfd;
193
194 setup_timer(0, timer_usec);
195
196 /* Set up listening socket, 'listen_sock' (socket(),
197 bind(), listen()) */
198
199 if(-1==(listen_sock=create_listener(host, port))) {
200 printf("create_listener(): failed\n");
201 return(1);
202 }
203
204 epollfd = epoll_create(10);
205 if (epollfd == -1) {
206 printf("epoll_create\n");
207 return(2);
208 }
209
210 ev.events = EPOLLIN;
211 ev.data.fd = listen_sock;
212 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
213 printf("epoll_ctl: listen_sock\n");
214 return(3);
215 }
216
217 for (;;) {
218 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 5000);
219 //printf("epoll event: nfds: %d\n", nfds);
220 if (nfds == -1) {
221 if(errno!=EINTR) {
222 printf("epoll_pwait\n");
223 return(4);
224 }
225 //printf("epoll_wait: interrupted\n");
226 nfds=0;
227 }
228
229 for (n = 0; n < nfds; ++n) {
230 if (events[n].data.fd == listen_sock) {
231
232 addrlen=sizeof(struct sockaddr);
233 conn_sock = accept(listen_sock,
234 (struct sockaddr *) &local, &addrlen);
235 if (conn_sock == -1) {
236 printf("accept: %s (%d)\n", strerror(errno), errno);
237 return(5);
238 }
239
240 fcntl(conn_sock, F_SETFL, O_NONBLOCK);
241
242 int yes=1;
243 if(0>setsockopt(conn_sock, SOL_TCP, TCP_NODELAY, &yes, sizeof(int))) {
244 printf("setsockopt\n");
245 continue;
246 }
247 if(0>setsockopt(conn_sock, SOL_TCP, TCP_CORK, &yes, sizeof(int))) {
248 printf("setsockopt\n");
249 return(6);
250 }
251
252 ev.events = EPOLLIN | EPOLLOUT | (et?EPOLLET:0) | EPOLLRDHUP;
253 ev.data.fd = conn_sock;
254 printf("epoll_ctl(add): events: %08X\n", ev.events);
255 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
256 &ev) == -1) {
257 printf("epoll_ctl: conn_sock\n");
258 return(7);
259 }
260
261 if(-1==(fd_in=open(fname, O_RDONLY)) || fstat(fd_in, &st)) {
262 printf("%s: %s (%d)\n", fname, strerror(errno), errno);
263 return(8);
264 }
265 offset=0;
266 size_left=st.st_size;
267
268 } else {
269 if(events[n].events & (EPOLLHUP|EPOLLRDHUP)) {
270 // Disconnect
271 printf("disconnect\n");
272 close(events[n].data.fd);
273 close(fd_in);
274 } else if(events[n].events & (EPOLLIN)) {
275 // Input
276 printf("got input, not supported\n");
277 } else if(events[n].events & (EPOLLOUT)) {
278 // Output
279 printf("can output: events: %08X\n", events[n].events);
280
281 if(0<(r=do_use_fd_fn(fd_in, &offset, &size_left, events[n].data.fd))) {
282 // Finished
283 printf("Finished: sent: %ld, left: %ld\n", offset, size_left);
284 close(events[n].data.fd);
285 close(fd_in);
286 } else if(!r) {
287 // Need more send
288 printf("Need more send: sent: %ld, left: %ld\n", offset, size_left);
289 } else {
290 // Error
291 printf("Sending file failed\n");
292 return(9);
293 }
294 } else {
295 // Other
296 }
297 }
298 }
299 }
300}