|  | /* | 
|  | * ttcp. Modelled after the unix ttcp | 
|  | * | 
|  | * Copyright (c) 2012, Bakul Shah <bakul@bitblocks.com> | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The author's name may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * This software is provided by the author AS IS.  The author DISCLAIMS | 
|  | * any and all warranties of merchantability and fitness for a particular | 
|  | * purpose.  In NO event shall the author be LIABLE for any damages | 
|  | * whatsoever arising in any way out of the use of this software. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Options not supported (may be supported in future): | 
|  | * +	-u	Use UDP instead of TCP | 
|  | *	-D	don't want for TCP send (TCP_NODELAY) | 
|  | *	-A num	align buffers on this boundary (default 16384) | 
|  | *	-O off	start buffers at this offset (default 0) | 
|  | * Misc: | 
|  | *	- print calls, msec/call calls/sec | 
|  | *	- print user/sys/real times | 
|  | * May be: | 
|  | *	- multicast support | 
|  | *	- isochronous transfer | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <fcntl.h> | 
|  | #include <assert.h> | 
|  | #include <parlib/net.h> | 
|  | #include <sys/time.h> | 
|  | #include <iplib/iplib.h> | 
|  | #include <parlib/timing.h> | 
|  |  | 
|  | long ncalls; | 
|  | char scale; | 
|  |  | 
|  | static void sysfatal(char *msg) | 
|  | { | 
|  | perror(msg); | 
|  | exit(-1); | 
|  | } | 
|  |  | 
|  | long nread(int fd, char *buf, long len) | 
|  | { | 
|  | int cnt, rlen = 0; | 
|  | char *b = buf; | 
|  |  | 
|  | for (;;) { | 
|  | cnt = read(fd, b, len); | 
|  | ncalls++; | 
|  | if (cnt <= 0) | 
|  | break; | 
|  | rlen += cnt; | 
|  | len -= cnt; | 
|  | if (len == 0) | 
|  | break; | 
|  | b += cnt; | 
|  | } | 
|  | return rlen; | 
|  | } | 
|  |  | 
|  | long nwrite(int fd, char *buf, long len) | 
|  | { | 
|  | int cnt, rlen = 0; | 
|  | char *b = buf; | 
|  |  | 
|  | for (;;) { | 
|  | cnt = write(fd, b, len); | 
|  | ncalls++; | 
|  | if (cnt <= 0) | 
|  | break; | 
|  | rlen += cnt; | 
|  | len -= cnt; | 
|  | if (len == 0) | 
|  | break; | 
|  | b += cnt; | 
|  | } | 
|  | return rlen; | 
|  | } | 
|  |  | 
|  | void pattern(char *buf, int buflen) | 
|  | { | 
|  | int i; | 
|  | char ch = ' '; | 
|  | char *b = buf; | 
|  |  | 
|  | for (i = 0; i < buflen; i++) { | 
|  | *b++ = ch++; | 
|  | if (ch == 127) | 
|  | ch = ' '; | 
|  | } | 
|  | } | 
|  |  | 
|  | char fmt = 'K'; | 
|  | char *unit; | 
|  |  | 
|  | double rate(long nbytes, double time) | 
|  | { | 
|  | switch (fmt) { | 
|  | case 'k': | 
|  | unit = "Kbit"; | 
|  | return nbytes * 8 / time / (1 << 10); | 
|  | case 'K': | 
|  | unit = "KB"; | 
|  | return nbytes / time / (1 << 10); | 
|  | case 'm': | 
|  | unit = "Mbit"; | 
|  | return nbytes * 8 / time / (1 << 20); | 
|  | case 'M': | 
|  | unit = "MB"; | 
|  | return nbytes / time / (1 << 20); | 
|  | case 'g': | 
|  | unit = "Gbit"; | 
|  | return nbytes * 8 / time / (1 << 30); | 
|  | case 'G': | 
|  | unit = "GB"; | 
|  | return nbytes / time / (1 << 30); | 
|  | } | 
|  | return 0.0; | 
|  | } | 
|  |  | 
|  | void reader(int udp, char *addr, char *port, int buflen, int nbuf, int sink) | 
|  | { | 
|  | char *buf, adir[40], ldir[40]; | 
|  | char *ds, ds_store[256]; | 
|  | int fd, cnt, acfd, lcfd; | 
|  | long nbytes = 0; | 
|  | long now; | 
|  | double elapsed; | 
|  | int pd; | 
|  | char peer[100]; | 
|  | double tput; | 
|  |  | 
|  | fprintf(stderr, "ttcp-r: buflen=%d, nbuf=%d, port=%s %s\n", | 
|  | buflen, nbuf, port, udp ? "udp" : "tcp"); | 
|  |  | 
|  | ds = netmkaddr(addr, udp ? "udp" : "tcp", port, ds_store, | 
|  | sizeof(ds_store)); | 
|  | acfd = announce9(ds, adir, 0); | 
|  | if (acfd < 0) | 
|  | sysfatal("announce: %r"); | 
|  | buf = malloc(buflen); | 
|  |  | 
|  | lcfd = listen9(adir, ldir, 0); | 
|  | if (lcfd < 0) | 
|  | sysfatal("listen: %r"); | 
|  |  | 
|  | fd = accept9(lcfd, ldir); | 
|  | if (fd < 0) | 
|  | return; | 
|  |  | 
|  | sprintf(peer, "%s/remote", ldir); | 
|  | pd = open(peer, O_READ); | 
|  | cnt = read(pd, peer, 100); | 
|  | close(pd); | 
|  |  | 
|  | fprintf(stderr, "ttcp-r: accept from %*.*s", cnt, cnt, peer); | 
|  | now = nsec(); | 
|  | if (sink) { | 
|  | while ((cnt = nread(fd, buf, buflen)) > 0) | 
|  | nbytes += cnt; | 
|  | } else { | 
|  | while ((cnt = nread(fd, buf, buflen)) > 0 | 
|  | && write(1, buf, cnt) == cnt) | 
|  | nbytes += cnt; | 
|  | } | 
|  | elapsed = (nsec() - now) / 1E9; | 
|  |  | 
|  | tput = rate(nbytes, elapsed);	/* also sets 'unit' */ | 
|  | fprintf(stderr, | 
|  | "ttcp-r: %lld bytes in %.2f real seconds = %.2f %s/sec\n", | 
|  | nbytes, elapsed, tput, unit); | 
|  | } | 
|  |  | 
|  | void writer(int udp, char *addr, char *port, int buflen, int nbuf, int src) | 
|  | { | 
|  | char *buf; | 
|  | int fd, cnt; | 
|  | long nbytes = 0; | 
|  | long now; | 
|  | double elapsed; | 
|  | char netaddr[128]; | 
|  | double tput; | 
|  |  | 
|  | fprintf(stderr, "ttcp-t: buflen=%d, nbuf=%d, port=%s %s -> %s\n", | 
|  | buflen, nbuf, port, udp ? "udp" : "tcp", addr); | 
|  |  | 
|  | buf = malloc(buflen); | 
|  | snprintf(netaddr, sizeof(netaddr), "%s!%s!%s", | 
|  | udp ? "udp" : "tcp", addr, port); | 
|  | fprintf(stderr, "dialing %s\n", netaddr); | 
|  | fd = dial9(netaddr, 0, 0, 0, 0); | 
|  | if (fd < 0) | 
|  | sysfatal("dial: %r"); | 
|  |  | 
|  | fprintf(stderr, "ttcp-t: connect\n"); | 
|  |  | 
|  | now = nsec(); | 
|  | if (src) { | 
|  | pattern(buf, buflen); | 
|  | while (nbuf-- && nwrite(fd, buf, buflen) == buflen) | 
|  | nbytes += buflen; | 
|  | } else { | 
|  | while ((cnt = read(0, buf, buflen)) > 0 | 
|  | && nwrite(fd, buf, cnt) == cnt) | 
|  | nbytes += cnt; | 
|  | } | 
|  | elapsed = (nsec() - now) / 1E9; | 
|  |  | 
|  | tput = rate(nbytes, elapsed);	/* also sets 'unit' */ | 
|  | fprintf(stderr, | 
|  | "ttcp-t: %lld bytes in %.2f real seconds = %.2f %s/sec\n", | 
|  | nbytes, elapsed, tput, unit); | 
|  | } | 
|  |  | 
|  | void usage(void) | 
|  | { | 
|  | fprintf(stderr, "usage:\tttcp -t [options] host\n" | 
|  | "\t\tttcp -r [options]\n" | 
|  | " options:\n" | 
|  | "  -f fmt\trate format: k,m,g,K,M,G = {kilo,mega,giga}{bit,byte}\n" | 
|  | "  -l\t\tlength of buf (default 8192)\n" | 
|  | "  -p port\tport number (default 5001)\n" | 
|  | "  -n num\tnumber of bufs written (default 2048)\n" | 
|  | "  -s\t\t-t: source a pattern to network\n" | 
|  | "\t\t-r: sink (discard) all data from network\n" | 
|  | ); | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | void main(int argc, char *argv[]) | 
|  | { | 
|  | int buflen = 8192; | 
|  | int nbuf = 2048; | 
|  | int srcsink = 0; | 
|  | char *port = "5001"; | 
|  | int udp = 0; | 
|  | enum { none, recv, xmit } mode = none; | 
|  | char c; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "rstuf:l:n:p:")) != -1) { | 
|  | switch (c) { | 
|  | case 'f': | 
|  | fmt = *optarg; | 
|  | break; | 
|  | case 'l': | 
|  | buflen = atoi(optarg); | 
|  | break; | 
|  | case 'n': | 
|  | nbuf = atoi(optarg); | 
|  | break; | 
|  | case 'p': | 
|  | port = optarg; | 
|  | break; | 
|  | case 'r': | 
|  | mode = recv; | 
|  | break; | 
|  | case 's': | 
|  | srcsink = 1; | 
|  | break; | 
|  | case 't': | 
|  | mode = xmit; | 
|  | break; | 
|  | case 'u': | 
|  | udp = 1; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | } | 
|  | } | 
|  | switch (mode) { | 
|  | case none: | 
|  | usage(); | 
|  | break; | 
|  | case xmit: | 
|  | if (optind == argc) | 
|  | usage(); | 
|  | writer(udp, argv[optind], port, buflen, nbuf, srcsink); | 
|  | break; | 
|  | case recv: | 
|  | reader(udp, "*", port, buflen, nbuf, srcsink); | 
|  | break; | 
|  | } | 
|  | exit(0); | 
|  | } |