Ticket #1174: sendfile_test.2.c

File sendfile_test.2.c, 7.7 KB (added by pokotilenko@…, 10 years ago)

Direct epoll/sendfile test with signal interruption

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 printf("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/* ****************************************************************** */
99void timer_handler(int signum) {
100
101 if(sending) {
102 printf("******************* Timer probably have happened while inside sendfile **********************\n");
103 }
104 //printf("Timer.\n");
105}
106
107/* ****************************************************************** */
108void setup_timer(long tv_sec, long tv_usec) {
109
110 struct itimerval new_value;
111
112 signal(SIGALRM, timer_handler);
113
114 new_value.it_interval.tv_sec=tv_sec;
115 new_value.it_interval.tv_usec=tv_usec;
116 new_value.it_value.tv_sec=tv_sec;
117 new_value.it_value.tv_usec=tv_usec;
118 setitimer(ITIMER_REAL, &new_value, NULL);
119}
120
121/* ****************************************************************** */
122int main(int argc, char **argv) {
123
124 char *host;
125 char *port;
126 char *fname;
127 int et=0;
128 long timer_usec;
129 int fd_in;
130 int n;
131 int r;
132
133 off_t offset;
134 size_t size_left;
135 struct stat st;
136
137 struct sockaddr_in local;
138 socklen_t addrlen;
139
140 if(argc<5 || argc>6 || !(timer_usec=atol(argv[4])) || (argc==6 && strcmp(argv[5], "et"))) {
141 printf("Usage: %s bind_addr bind_port file_name timer_usec [et]\n", argv[0]);
142 printf("\ttimer_usec: set timer to timer_usec microseconds, programm is signalled with SIGALRM each time timer expires\n");
143 printf("\tet: if set use edge triggered epoll mode\n");
144 return(1);
145 }
146
147 host=argv[1];
148 port=argv[2];
149 fname=argv[3];
150 if(argc==6) et=1;
151
152 printf("Serving file: %s\n", fname);
153 printf("Timer microseconds: %lu\n", timer_usec);
154 printf("Edge triggered mode: %s\n", et?"set":"not set");
155
156#define MAX_EVENTS 10
157 struct epoll_event ev, events[MAX_EVENTS];
158 int listen_sock, conn_sock, nfds, epollfd;
159
160 setup_timer(0, timer_usec);
161
162 /* Set up listening socket, 'listen_sock' (socket(),
163 bind(), listen()) */
164
165 if(-1==(listen_sock=create_listener(host, port))) {
166 printf("create_listener(): failed\n");
167 return(1);
168 }
169
170 epollfd = epoll_create(10);
171 if (epollfd == -1) {
172 printf("epoll_create\n");
173 return(2);
174 }
175
176 ev.events = EPOLLIN;
177 ev.data.fd = listen_sock;
178 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
179 printf("epoll_ctl: listen_sock\n");
180 return(3);
181 }
182
183 for (;;) {
184 nfds = epoll_wait(epollfd, events, MAX_EVENTS, 5000);
185 //printf("epoll event: nfds: %d\n", nfds);
186 if (nfds == -1) {
187 if(errno!=EINTR) {
188 printf("epoll_pwait\n");
189 return(4);
190 }
191 //printf("epoll_wait: interrupted\n");
192 nfds=0;
193 }
194
195 for (n = 0; n < nfds; ++n) {
196 if (events[n].data.fd == listen_sock) {
197
198 addrlen=sizeof(struct sockaddr);
199 conn_sock = accept(listen_sock,
200 (struct sockaddr *) &local, &addrlen);
201 if (conn_sock == -1) {
202 printf("accept: %s (%d)\n", strerror(errno), errno);
203 return(5);
204 }
205
206 fcntl(conn_sock, F_SETFL, O_NONBLOCK);
207
208 int yes=1;
209 if(0>setsockopt(conn_sock, SOL_TCP, TCP_NODELAY, &yes, sizeof(int))) {
210 printf("setsockopt\n");
211 continue;
212 }
213 if(0>setsockopt(conn_sock, SOL_TCP, TCP_CORK, &yes, sizeof(int))) {
214 printf("setsockopt\n");
215 return(6);
216 }
217
218 ev.events = EPOLLIN | EPOLLOUT | (et?EPOLLET:0) | EPOLLRDHUP;
219 ev.data.fd = conn_sock;
220 printf("epoll_ctl(add): events: %08X\n", ev.events);
221 if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
222 &ev) == -1) {
223 printf("epoll_ctl: conn_sock\n");
224 return(7);
225 }
226
227 if(-1==(fd_in=open(fname, O_RDONLY)) || fstat(fd_in, &st)) {
228 printf("%s: %s (%d)\n", fname, strerror(errno), errno);
229 return(8);
230 }
231 offset=0;
232 size_left=st.st_size;
233
234 } else {
235 if(events[n].events & (EPOLLHUP|EPOLLRDHUP)) {
236 // Disconnect
237 printf("disconnect\n");
238 close(events[n].data.fd);
239 close(fd_in);
240 } else if(events[n].events & (EPOLLIN)) {
241 // Input
242 printf("got input, not supported\n");
243 } else if(events[n].events & (EPOLLOUT)) {
244 // Output
245 printf("can output: events: %08X\n", events[n].events);
246
247 if(0<(r=do_use_fd(fd_in, &offset, &size_left, events[n].data.fd))) {
248 // Finished
249 printf("Finished: sent: %ld, left: %ld\n", offset, size_left);
250 close(events[n].data.fd);
251 close(fd_in);
252 } else if(!n) {
253 // Need more send
254 printf("Need more send: sent: %ld, left: %ld\n", offset, size_left);
255 } else {
256 // Error
257 printf("Sending file failed\n");
258 return(9);
259 }
260 } else {
261 // Other
262 }
263 }
264 }
265 }
266}