Import ttcp for Plan 9
[akaros.git] / tests / ttcp.c
1 /*
2  * ttcp. Modelled after the unix ttcp
3  *
4  * Copyright (c) 2012, Bakul Shah <bakul@bitblocks.com>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The author's name may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  * 
18  * This software is provided by the author AS IS.  The author DISCLAIMS
19  * any and all warranties of merchantability and fitness for a particular
20  * purpose.  In NO event shall the author be LIABLE for any damages
21  * whatsoever arising in any way out of the use of this software.
22  */
23
24 /*
25  * Options not supported (may be supported in future):
26  * +    -u      Use UDP instead of TCP
27  *      -D      don't want for TCP send (TCP_NODELAY)
28  *      -A num  align buffers on this boundary (default 16384)
29  *      -O off  start buffers at this offset (default 0)
30  * Misc:
31  *      - print calls, msec/call calls/sec
32  *      - print user/sys/real times
33  * May be:
34  *      - multicast support
35  *      - isochronous transfer
36  */
37
38 #include <u.h>
39 #include <libc.h>
40
41 long ncalls;
42 char scale;
43
44 long
45 nread(int fd, char* buf, long len)
46 {
47         int cnt, rlen = 0;
48         char* b = buf;
49         for (;;) {
50                 cnt = read(fd, b, len);
51                 ncalls++;
52                 if (cnt <= 0)
53                         break;
54                 rlen += cnt;
55                 len -= cnt;
56                 if (len == 0)
57                         break;
58                 b += cnt;
59         }
60         return rlen;
61 }
62
63 long
64 nwrite(int fd, char* buf, long len)
65 {
66         int cnt, rlen = 0;
67         char* b = buf;
68         for (;;) {
69                 cnt = write(fd, b, len);
70                 ncalls++;
71                 if (cnt <= 0)
72                         break;
73                 rlen += cnt;
74                 len -= cnt;
75                 if (len == 0)
76                         break;
77                 b += cnt;
78         }
79         return rlen;
80 }
81
82 void
83 pattern(char* buf, int buflen)
84 {
85         int i;
86         char ch = ' ';
87         char* b = buf;
88         for (i = 0; i < buflen; i++) {
89                 *b++ = ch++;
90                 if (ch == 127)
91                         ch = ' ';
92         }
93 }
94
95 char fmt = 'K';
96 char* unit;
97
98 double
99 rate(vlong nbytes, double time)
100 {
101         switch (fmt) {
102         case 'k':
103                 unit = "Kbit";
104                 return nbytes*8/time/(1<<10);
105         case 'K':
106                 unit = "KB";
107                 return nbytes/time/(1<<10);
108         case 'm':
109                 unit = "Mbit";
110                 return nbytes*8/time/(1<<20);
111         case 'M':
112                 unit = "MB";
113                 return nbytes/time/(1<<20);
114         case 'g':
115                 unit = "Gbit";
116                 return nbytes*8/time/(1<<30);
117         case 'G':
118                 unit = "GB";
119                 return nbytes/time/(1<<30);
120         }
121         return 0.0;
122 }
123
124 void
125 reader(int udp, char* addr, char* port, int buflen, int nbuf, int sink)
126 {
127         char *buf, adir[40], ldir[40];
128         int fd, cnt, acfd, lcfd;
129         vlong nbytes = 0;
130         vlong now;
131         double elapsed;
132         int pd;
133         char peer[100];
134
135
136         print("ttcp-r: buflen=%d, nbuf=%d, port=%s %s\n",
137                 buflen, nbuf, port, udp? "udp" : "tcp");
138
139         acfd = announce(netmkaddr(addr, udp? "udp": "tcp", port), adir);
140         if(acfd < 0)
141                 sysfatal("announce: %r");
142         buf = malloc(buflen);
143
144         lcfd = listen(adir, ldir);
145         if(lcfd < 0)
146                 sysfatal("listen: %r");
147
148         fd = accept(lcfd, ldir);
149         if(fd < 0)
150                 return;
151
152         sprint(peer, "%s/remote", ldir);
153         pd = open(peer, OREAD);
154         cnt = read(pd, peer, 100);
155         close(pd);
156
157         print("ttcp-r: accept from %*.*s", cnt, cnt, peer);
158         now = nsec();
159         if (sink) {
160                 while((cnt = nread(fd, buf, buflen)) > 0)
161                         nbytes += cnt;
162         } else {
163                 while((cnt = nread(fd, buf, buflen)) > 0 &&
164                       write(1, buf, cnt) == cnt)
165                         nbytes += cnt;
166         }
167         elapsed = (nsec() - now)/1E9;
168
169         print("ttcp-r: %lld bytes in %.2f real seconds = %.2f %s/sec\n",
170               nbytes, elapsed, rate(nbytes, elapsed), unit);
171 }
172
173 void
174 writer(int udp, char* addr, char* port, int buflen, int nbuf, int src)
175 {
176         char *buf;
177         int fd, cnt;
178         vlong nbytes = 0;
179         vlong now;
180         double elapsed;
181
182         print("ttcp-t: buflen=%d, nbuf=%d, port=%s %s -> %s\n",
183                 buflen, nbuf, port, udp? "udp" : "tcp", addr);
184
185         buf = malloc(buflen);
186         fd = dial(netmkaddr(addr, udp? "udp" : "tcp", port), 0, 0, 0);
187         if(fd < 0)
188                 sysfatal("dial: %r");
189
190         print("ttcp-t: connect\n");
191
192         now = nsec();
193         if (src) {
194                 pattern(buf, buflen);
195                 while (nbuf-- && nwrite(fd, buf, buflen) == buflen)
196                         nbytes += buflen;
197         } else {
198                 while ((cnt = read(0, buf, buflen)) > 0 &&
199                        nwrite(fd, buf, cnt) == cnt)
200                         nbytes += cnt;
201         }
202         elapsed = (nsec() - now)/1E9;
203
204         print("ttcp-t: %lld bytes in %.2f real seconds = %.2f %s/sec\n",
205               nbytes, elapsed, rate(nbytes, elapsed), unit);
206 }
207
208 void
209 usage(void)
210 {
211         print("usage:\tttcp -t [options] host\n"
212               "\t\tttcp -r [options]\n"
213               " options:\n"
214 //            "  -D\t\don't delay tcp (nodelay option)\n"
215               "  -f fmt\trate format: k,m,g,K,M,G = {kilo,mega,giga}{bit,byte}\n"
216               "  -l\t\tlength of buf (default 8192)\n"
217               "  -p port\tport number (default 5001)\n"
218               "  -n num\tnumber of bufs written (default 2048)\n"
219               "  -s\t\t-t: source a pattern to network\n"
220               "\t\t\-r: sink (discard) all data from network\n"
221 //            "  -u\t\tuse UDP instead of TCP\n"
222               );
223         exits(0);
224 }
225
226 void
227 main(int argc, char *argv[])
228 {
229         int buflen = 8192;
230         int nbuf = 2048;
231         int srcsink = 0;
232         char* port = "5001";
233         int udp = 0;
234         enum {none, recv, xmit} mode = none;
235
236         ARGBEGIN {
237         case 'f':
238                 fmt = EARGF(usage())[0];
239                 break;
240         case 'l':
241                 buflen = atoi(EARGF(usage()));
242                 break;
243         case 'n':
244                 nbuf = atoi(EARGF(usage()));
245                 break;
246         case 'p':
247                 port = EARGF(usage());
248                 break;
249         case 'r':
250                 mode = recv;
251                 break;
252         case 's':
253                 srcsink = 1;
254                 break;
255         case 't':
256                 mode = xmit;
257                 break;
258         case 'u':
259                 udp = 1;
260                 break;
261         default:
262                 usage();
263         } ARGEND;
264
265         USED(buflen);
266         switch (mode) {
267         case none:
268                 usage();
269                 break;
270         case xmit:
271                 if (argv[0] == nil)
272                         usage();
273                 writer(udp, argv[0], port, buflen, nbuf, srcsink);
274                 break;
275         case recv:
276                 reader(udp, "*", port, buflen, nbuf, srcsink);
277                 break;
278         }
279         exits(0);
280 }