| /* |
| * 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); |
| } |