3 * tcpblast - test and estimate TCP thruput
5 * Daniel Karrenberg <dfk@nic.eu.net>
9 * Changes: Rafal Maszkowski <rzm@icm.edu.pl>
11 * ftp://6bone-gw.6bone.pl/pub/blast/README
16 , verstr[30]="FreeBSD + rzm ";
18 #include <sys/types.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
26 #include <strings.h> /* for Solaris */
28 #include <sys/socket.h>
33 #if defined(__sunos__) || defined(__osf1__)
34 #define strerror(x) atoi(x)
35 char * gettext(x) char *x; { return x; }
38 #define DEFBLKSIZE (1024)
39 #define MAXBLKSIZE (32*1024)
41 struct sockaddr_in sock_in;
45 unsigned long starts, startms, stops, stopms, expms;
49 char greet[MAXBLKSIZE], *ind;
51 int tcp=0, udp=0, randomb=0, blksize=DEFBLKSIZE, setbufsize=-1, dots=1, continuous=0, experimental=0;
55 static const struct option long_options[] =
57 { "help", no_argument, NULL, 'h' },
58 { "version", no_argument, NULL, 'V' },
65 fprintf(stderr, "\n");
66 fprintf(stderr, "usage: %s [options] destination nblocks\n\n", name);
67 fprintf(stderr, "tcpblast/udpblast is a simple tool for probing network and estimating its\n");
68 fprintf(stderr, "throughput. It sends nblocks of %d B blocks of data to specified\n", blksize);
69 fprintf(stderr, "destination host\n\n");
70 fprintf(stderr, "Options:\n");
71 fprintf(stderr, "-b nnn socket buf size (default: %d == %s)\n", setbufsize, setbufsize==-1 ? "don't change" : "change");
72 fprintf(stderr, "-c display speed continuously\n");
73 fprintf(stderr, "-d nnn print dot every nnn blocks, 0 disables (default %d)\n", dots);
74 fprintf(stderr, "-e add experimental way of calculating throuput\n");
75 /* fprintf(stderr, "-f FILE send FILE instead of generated data\n"); */
76 fprintf(stderr, "-h, --help this help\n");
77 fprintf(stderr, "-p xyz use port #/name xyz instead of default %s\n", port);
78 fprintf(stderr, "-r send random data\n");
79 fprintf(stderr, "-s nnn block size (default %d bytes)\n", blksize);
80 fprintf(stderr, "-t use TCP (%s)\n", ind[0]=='t' ? "default" : "default if named tcpblast" );
81 fprintf(stderr, "-u use UDP (%s)\n", ind[0]=='u' ? "default" : "default if named udpblast" );
82 fprintf(stderr, "-V, --version version\n");
83 fprintf(stderr, "destination host name or address\n");
84 fprintf(stderr, "nblocks number of blocks (1..9999)\n");
85 fprintf(stderr, "\n");
86 fprintf(stderr, "%s version: %s\n", name, verstr);
90 void usage_small(name)
93 fprintf(stderr, "type %s --help for help\n", name);
96 /* randomize the buffer */
102 for (i=0; i<blksize; i++) {
103 greet[i]=rand() % 256;
110 if (gettimeofday(&ti, &tiz) < 0)
112 perror("tcp/udpblast time:");
116 stopms = ti.tv_usec / 1000L;
118 return (stops-starts)*1000 + (stopms-startms);
122 printresult(expms, datasize, buffer)
124 int datasize, buffer;
126 printf("%5d KB in %7ld msec", datasize/1024, expms);
128 /* Use integer arithmetic only. */
129 printf(" = %8lld b/s", (unsigned long long) (datasize-buffer) * 8000 / expms);
130 printf(" = %7lld B/s", (unsigned long long) (datasize-buffer) * 1000 / expms);
131 printf(" = %7lld KB/s",
132 (unsigned long long) (datasize-buffer) * 1000 / (expms * 1024) );
134 printf(" = %8.1f b/s", ((double)datasize-buffer)/expms*8000);
135 printf(" = %7.1f B/s", ((double)datasize-buffer)/expms*1000);
136 printf(" = %7.2f KB/s",
137 ((double)datasize-buffer) / (double) (expms*1024.0) * 1000 );
148 struct servent *service;
151 strcat(verstr, vdate);
153 /* non-random data - is modem compressing it? */
154 bzero(greet, MAXBLKSIZE);
155 memset(greet, 'a', MAXBLKSIZE);
157 /* find first letter in the name - usage() needs it */
158 ind=rindex(argv[0], '/');
159 if (ind==NULL) ind=argv[0]; else ind++;
161 while ((optchar = getopt_long (argc, argv, "tup:rs:b:d:Vhc", long_options, NULL)) != EOF)
164 case 't': if (tcp==0) tcp=1; break;
165 case 'u': if (udp==0) udp=1; break;
166 case 'r': srand(0 /* may be an option */); randomb=1; break;
167 case 's': blksize=abs(atoi(optarg)); break;
168 case 'b': setbufsize=abs(atoi(optarg)); break;
169 case 'c': continuous=1; break;
170 case 'd': dots=abs(atoi(optarg)); break;
171 case 'e': experimental=1; break;
172 case 'p': strncpy(port, optarg, sizeof(port)-1); break;
173 case 'V': printf("%s version: %s\n", argv[0], verstr); return 0; break;
174 case 'h': usage(argv[0]);
180 printf("cannot use both TCP and UDP\n");
181 usage_small(argv[0]);
185 /* if neither -t nor -u is chosen use first character of the
187 if ( (tcp==0) && (udp==0) && (ind[0]=='t') ) tcp=1;
188 if ( (tcp==0) && (udp==0) && (ind[0]=='u') ) udp=1;
191 printf("must use either TCP or UDP\n");
192 usage_small(argv[0]);
196 if (continuous) dots=0;
198 /* after options processing we need two args left */
199 if (argc - optind != 2) {
200 if (argc - optind != 0) printf("give both hostname and block count\n");
201 usage_small(argv[0]);
205 nblocks = atoi(argv[optind+1]);
206 if (nblocks<=0 || nblocks>=INT_MAX) {
207 fprintf(stderr, "%s: 1 < nblocks <= %d \n", argv[0], INT_MAX);
212 bzero((char *)&sock_in, sizeof (sock_in));
213 sock_in.sin_family = AF_INET;
214 if (tcp) f = socket(AF_INET, SOCK_STREAM, 0);
215 else f = socket(AF_INET, SOCK_DGRAM, 0);
217 perror("tcp/udpblast: socket");
221 { int size=sizeof(int); /* getsockopt() should know how much space we have */
222 /* get/setsockopt doesn't return any error really for SO_SNDBUF,
223 at least on Linux; it limits the buffer to [2048..65536]
224 (131070 for 2.1 (?) but you can manipulate with /proc/sys/net/core/wmem_max ) */
225 if (getsockopt(f, SOL_SOCKET, SO_SNDBUF, &bufsize, &size)==-1)
226 printf("tcp/udpblast getsockopt: %s", strerror(errno));
227 printf("read SO_SNDBUF = %d\n", bufsize);
228 if (setbufsize!=-1) {
229 if (setsockopt(f, SOL_SOCKET, SO_SNDBUF, &setbufsize, sizeof(setbufsize))==-1)
230 printf("tcp/udpblast getsockopt: %s", strerror(errno));
231 if (getsockopt(f, SOL_SOCKET, SO_SNDBUF, &bufsize, &size)==-1) /* size value's been set b4 */
232 printf("tcp/udpblast getsockopt: %s", strerror(errno));
233 printf("set SO_SNDBUF = %d\n", bufsize);
237 if (bind(f, (struct sockaddr*)&sock_in, sizeof (sock_in)) < 0) {
238 perror("tcp/udpblast: bind");
242 host = gethostbyname(argv[optind]);
244 sock_in.sin_family = host->h_addrtype;
245 bcopy(host->h_addr, &sock_in.sin_addr, host->h_length);
247 sock_in.sin_family = AF_INET;
248 sock_in.sin_addr.s_addr = inet_addr(argv[optind]);
249 if (sock_in.sin_addr.s_addr == -1) {
250 fprintf(stderr, "%s: %s unknown host\n", argv[0], argv[optind]);
255 /* port # or name can be used */
256 service = getservbyname(port, tcp ? "tcp" : "udp");
257 if (service==NULL) sock_in.sin_port = htons(abs(atoi(port)));
258 else sock_in.sin_port = service->s_port;
260 if (connect(f, (struct sockaddr*)&sock_in, sizeof(sock_in)) <0)
262 perror("tcp/udpblast connect:");
266 printf("Sending %s %s data using %d B blocks.\n",
267 randomb ? "random":"non-random", tcp ? "TCP":"UDP", blksize);
269 if (gettimeofday(&ti, &tiz) < 0)
271 perror("tcp/udpblast time:");
275 startms = ti.tv_usec / 1000L;
278 for (i=0; i<nblocks; i++)
280 if (randomb) randbuff(blksize);
281 if (write(f, greet, (size_t)blksize) != blksize)
282 perror("tcp/udpblast send:");
283 if ( (dots!=0) && ( (dots==1) || (i%dots==1) ) ) write(1, ".", 1);
284 if ( continuous == 1 ) {
287 printresult(expms, i*blksize, experimental ? bufsize : 0);
292 if (dots!=0) printf("\n");
293 if (continuous) printf("\r");
294 printresult(expms, nblocks*blksize, 0);
295 if (experimental && (bufsize>0) && (nblocks*blksize>bufsize)) {
296 printf("\nExperimentally taking into account %d B socket buffer:\n", bufsize);
297 printresult(expms, nblocks*blksize, bufsize);