44253806fd7e4d76dc3c1a5cd0d01617c66e8d4e
[tridge/junkcode.git] / messaging / tcp.c
1 /* 
2    measure latency of tcp sockets
3    tridge@samba.org July 2006
4 */
5
6 #define _GNU_SOURCE
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdarg.h>
11 #include <fcntl.h>
12 #include <sys/ioctl.h>
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <sys/poll.h>
17 #include <sys/time.h>
18 #include <time.h>
19 #include <errno.h>
20 #include <netinet/in.h>
21 #include <netinet/tcp.h>
22 #include <netdb.h>
23 #include <arpa/inet.h>
24
25
26 typedef int BOOL;
27
28 #define True 1
29 #define False 0
30
31 static struct timeval tp1,tp2;
32
33 static void start_timer()
34 {
35         gettimeofday(&tp1,NULL);
36 }
37
38 static double end_timer()
39 {
40         gettimeofday(&tp2,NULL);
41         return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
42                 (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
43 }
44
45 static void fatal(const char *why)
46 {
47         fprintf(stderr, "fatal: %s - %s\n", why, strerror(errno));
48         exit(1);
49 }
50
51 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
52
53 struct
54 {
55   char *name;
56   int level;
57   int option;
58   int value;
59   int opttype;
60 } socket_options[] = {
61   {"SO_KEEPALIVE",      SOL_SOCKET,    SO_KEEPALIVE,    0,                 OPT_BOOL},
62   {"SO_REUSEADDR",      SOL_SOCKET,    SO_REUSEADDR,    0,                 OPT_BOOL},
63   {"SO_BROADCAST",      SOL_SOCKET,    SO_BROADCAST,    0,                 OPT_BOOL},
64 #ifdef TCP_NODELAY
65   {"TCP_NODELAY",       IPPROTO_TCP,   TCP_NODELAY,     0,                 OPT_BOOL},
66 #endif
67 #ifdef IPTOS_LOWDELAY
68   {"IPTOS_LOWDELAY",    IPPROTO_IP,    IP_TOS,          IPTOS_LOWDELAY,    OPT_ON},
69 #endif
70 #ifdef IPTOS_THROUGHPUT
71   {"IPTOS_THROUGHPUT",  IPPROTO_IP,    IP_TOS,          IPTOS_THROUGHPUT,  OPT_ON},
72 #endif
73 #ifdef SO_SNDBUF
74   {"SO_SNDBUF",         SOL_SOCKET,    SO_SNDBUF,       0,                 OPT_INT},
75 #endif
76 #ifdef SO_RCVBUF
77   {"SO_RCVBUF",         SOL_SOCKET,    SO_RCVBUF,       0,                 OPT_INT},
78 #endif
79 #ifdef SO_SNDLOWAT
80   {"SO_SNDLOWAT",       SOL_SOCKET,    SO_SNDLOWAT,     0,                 OPT_INT},
81 #endif
82 #ifdef SO_RCVLOWAT
83   {"SO_RCVLOWAT",       SOL_SOCKET,    SO_RCVLOWAT,     0,                 OPT_INT},
84 #endif
85 #ifdef SO_SNDTIMEO
86   {"SO_SNDTIMEO",       SOL_SOCKET,    SO_SNDTIMEO,     0,                 OPT_INT},
87 #endif
88 #ifdef SO_RCVTIMEO
89   {"SO_RCVTIMEO",       SOL_SOCKET,    SO_RCVTIMEO,     0,                 OPT_INT},
90 #endif
91   {NULL,0,0,0,0}};
92
93
94 /****************************************************************************
95   Get the next token from a string, return False if none found
96   handles double-quotes. 
97 Based on a routine by GJC@VILLAGE.COM. 
98 Extensively modified by Andrew.Tridgell@anu.edu.au
99 ****************************************************************************/
100 BOOL next_token(char **ptr,char *buff,char *sep)
101 {
102   char *s;
103   BOOL quoted;
104   static char *last_ptr=NULL;
105
106   if (!ptr) ptr = &last_ptr;
107   if (!ptr) return(False);
108
109   s = *ptr;
110
111   /* default to simple separators */
112   if (!sep) sep = " \t\n\r";
113
114   /* find the first non sep char */
115   while(*s && strchr(sep,*s)) s++;
116
117   /* nothing left? */
118   if (! *s) return(False);
119
120   /* copy over the token */
121   for (quoted = False; *s && (quoted || !strchr(sep,*s)); s++)
122     {
123       if (*s == '\"') 
124         quoted = !quoted;
125       else
126         *buff++ = *s;
127     }
128
129   *ptr = (*s) ? s+1 : s;  
130   *buff = 0;
131   last_ptr = *ptr;
132
133   return(True);
134 }
135         
136
137 /****************************************************************************
138 set user socket options
139 ****************************************************************************/
140 void set_socket_options(int fd, char *options)
141 {
142         char tok[200];
143
144         while (next_token(&options,tok," \t,"))
145     {
146       int ret=0,i;
147       int value = 1;
148       char *p;
149       BOOL got_value = False;
150
151       if ((p = strchr(tok,'=')))
152         {
153           *p = 0;
154           value = atoi(p+1);
155           got_value = True;
156         }
157
158       for (i=0;socket_options[i].name;i++)
159         if (strcasecmp(socket_options[i].name,tok)==0)
160           break;
161
162       if (!socket_options[i].name)
163         {
164           printf("Unknown socket option %s\n",tok);
165           continue;
166         }
167
168       switch (socket_options[i].opttype)
169         {
170         case OPT_BOOL:
171         case OPT_INT:
172           ret = setsockopt(fd,socket_options[i].level,
173                            socket_options[i].option,(char *)&value,sizeof(int));
174           break;
175
176         case OPT_ON:
177                 if (got_value)
178                         printf("syntax error - %s does not take a value\n",tok);
179
180           {
181             int on = socket_options[i].value;
182             ret = setsockopt(fd,socket_options[i].level,
183                              socket_options[i].option,(char *)&on,sizeof(int));
184           }
185           break;          
186         }
187       
188       if (ret != 0)
189         printf("Failed to set socket option %s\n",tok);
190     }
191 }
192
193 /*
194   connect to a tcp socket
195 */
196 int tcp_socket_connect(int port)
197 {
198         int type = SOCK_STREAM;
199         struct sockaddr_in sock_out;
200         int res;
201         struct hostent *hp;
202         const char *host = "localhost";
203
204         res = socket(PF_INET, type, 0);
205         if (res == -1) {
206                 return -1;
207         }
208
209         hp = gethostbyname(host);
210         if (!hp) {
211                 fprintf(stderr,"unknown host: %s\n", host);
212                 close(res);
213                 return -1;
214         }
215
216         memcpy(&sock_out.sin_addr, hp->h_addr, hp->h_length);
217         sock_out.sin_port = htons(port);
218         sock_out.sin_family = PF_INET;
219
220         if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
221                 fprintf(stderr,"failed to connect to %s - %s\n", host, strerror(errno));
222                 close(res);
223                 return -1;
224         }
225
226         return res;
227 }
228
229
230 /*
231   create a tcp socket and bind it
232   return a file descriptor open on the socket 
233 */
234 static int tcp_socket_bind(int port)
235 {
236         struct hostent *hp;
237         struct sockaddr_in sock;
238         char host_name[1000];
239         int res;
240         int one=1;
241
242         /* get my host name */
243         if (gethostname(host_name, sizeof(host_name)) == -1) { 
244                 fprintf(stderr,"gethostname failed\n"); 
245                 return -1; 
246         } 
247
248         /* get host info */
249         if ((hp = gethostbyname(host_name)) == 0) {
250                 fprintf(stderr,"gethostbyname: Unknown host %s\n",host_name);
251                 return -1;
252         }
253   
254         memset((char *)&sock,0,sizeof(sock));
255         memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);
256         sock.sin_port = htons(port);
257         sock.sin_family = hp->h_addrtype;
258         sock.sin_addr.s_addr = INADDR_ANY;
259         res = socket(hp->h_addrtype, SOCK_STREAM, 0);
260         if (res == -1) { 
261                 fprintf(stderr,"socket failed\n"); 
262                 return -1; 
263         }
264
265         setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one));
266
267         /* now we've got a socket - we need to bind it */
268         if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) == -1) { 
269                 fprintf(stderr,"bind failed on port %d\n", port);
270                 close(res); 
271                 return -1;
272         }
273
274         listen(res, 1);
275
276         return res;
277 }
278
279 /*
280   accept on a tcp socket
281 */
282 static int tcp_socket_accept(int fd)
283 {
284         struct sockaddr a;
285         socklen_t len;
286         return accept(fd, &a, &len);
287 }
288
289 static void worker(int port1, int port2, int w)
290 {
291         int l = tcp_socket_bind(port1);
292         int s2, s1;
293         int count=0;
294
295         set_socket_options(l, "SO_REUSEADDR");
296
297         sleep(2);
298
299         s2 = tcp_socket_connect(port2);
300         s1 = tcp_socket_accept(l);
301
302         set_socket_options(s1, "TCP_NODELAY");
303         set_socket_options(s2, "TCP_NODELAY");
304
305         start_timer();
306
307         while (1) {
308                 char c=0;
309                 if (write(s2, &c, 1) != 1) {
310                         fatal("write");
311                 }
312                 if (read(s1, &c, 1) != 1) {
313                         fatal("read");
314                 }
315                 if (w == 1 && (end_timer() > 1.0)) {
316                         printf("%8u ops/sec\r", 
317                                (unsigned)(2*count/end_timer()));
318                         fflush(stdout);
319                         start_timer();
320                         count=0;
321                 }
322                 count++;
323         }
324 }
325
326 int main(int argc, char *argv[])
327 {
328         if (fork() == 0) {
329                 worker(1313, 1314, 0);
330         } else {
331                 worker(1314, 1313, 1);
332         }
333         return 0;
334 }