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