2
measure latency of tcp sockets
3
tridge@samba.org July 2006
12
#include <sys/ioctl.h>
13
#include <sys/types.h>
14
#include <sys/socket.h>
20
#include <netinet/in.h>
21
#include <netinet/tcp.h>
23
#include <arpa/inet.h>
31
static struct timeval tp1,tp2;
33
static void start_timer()
35
gettimeofday(&tp1,NULL);
38
static double end_timer()
40
gettimeofday(&tp2,NULL);
41
return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
42
(tp1.tv_sec + (tp1.tv_usec*1.0e-6));
45
static void fatal(const char *why)
47
fprintf(stderr, "fatal: %s - %s\n", why, strerror(errno));
51
enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
60
} socket_options[] = {
61
{"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
62
{"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
63
{"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
65
{"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
68
{"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
70
#ifdef IPTOS_THROUGHPUT
71
{"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
74
{"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
77
{"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
80
{"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
83
{"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
86
{"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
89
{"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
94
/****************************************************************************
95
Get the next token from a string, return False if none found
96
handles double-quotes.
97
Based on a routine by GJC@VILLAGE.COM.
98
Extensively modified by Andrew.Tridgell@anu.edu.au
99
****************************************************************************/
100
BOOL next_token(char **ptr,char *buff,char *sep)
104
static char *last_ptr=NULL;
106
if (!ptr) ptr = &last_ptr;
107
if (!ptr) return(False);
111
/* default to simple separators */
112
if (!sep) sep = " \t\n\r";
114
/* find the first non sep char */
115
while(*s && strchr(sep,*s)) s++;
118
if (! *s) return(False);
120
/* copy over the token */
121
for (quoted = False; *s && (quoted || !strchr(sep,*s)); s++)
129
*ptr = (*s) ? s+1 : s;
137
/****************************************************************************
138
set user socket options
139
****************************************************************************/
140
void set_socket_options(int fd, char *options)
144
while (next_token(&options,tok," \t,"))
149
BOOL got_value = False;
151
if ((p = strchr(tok,'=')))
158
for (i=0;socket_options[i].name;i++)
159
if (strcasecmp(socket_options[i].name,tok)==0)
162
if (!socket_options[i].name)
164
printf("Unknown socket option %s\n",tok);
168
switch (socket_options[i].opttype)
172
ret = setsockopt(fd,socket_options[i].level,
173
socket_options[i].option,(char *)&value,sizeof(int));
178
printf("syntax error - %s does not take a value\n",tok);
181
int on = socket_options[i].value;
182
ret = setsockopt(fd,socket_options[i].level,
183
socket_options[i].option,(char *)&on,sizeof(int));
189
printf("Failed to set socket option %s\n",tok);
194
connect to a tcp socket
196
int tcp_socket_connect(const char *host, int port)
198
int type = SOCK_STREAM;
199
struct sockaddr_in sock_out;
203
res = socket(PF_INET, type, 0);
208
hp = gethostbyname(host);
210
fprintf(stderr,"unknown host: %s\n", host);
215
memcpy(&sock_out.sin_addr, hp->h_addr, hp->h_length);
216
sock_out.sin_port = htons(port);
217
sock_out.sin_family = PF_INET;
219
if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) {
220
fprintf(stderr,"failed to connect to %s - %s\n", host, strerror(errno));
230
create a tcp socket and bind it
231
return a file descriptor open on the socket
233
static int tcp_socket_bind(int port)
236
struct sockaddr_in sock;
237
char host_name[1000];
241
/* get my host name */
242
if (gethostname(host_name, sizeof(host_name)) == -1) {
243
fprintf(stderr,"gethostname failed\n");
248
if ((hp = gethostbyname(host_name)) == 0) {
249
fprintf(stderr,"gethostbyname: Unknown host %s\n",host_name);
253
memset((char *)&sock,0,sizeof(sock));
254
memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length);
255
sock.sin_port = htons(port);
256
sock.sin_family = hp->h_addrtype;
257
sock.sin_addr.s_addr = INADDR_ANY;
258
res = socket(hp->h_addrtype, SOCK_STREAM, 0);
260
fprintf(stderr,"socket failed\n");
264
setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one));
266
/* now we've got a socket - we need to bind it */
267
if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) == -1) {
268
fprintf(stderr,"bind failed on port %d\n", port);
279
accept on a tcp socket
281
static int tcp_socket_accept(int fd)
285
return accept(fd, &a, &len);
288
static void worker(const char *host, int port1, int port2, int w)
290
int l = tcp_socket_bind(port1);
294
set_socket_options(l, "SO_REUSEADDR");
298
s2 = tcp_socket_connect(host, port2);
299
s1 = tcp_socket_accept(l);
301
set_socket_options(s1, "TCP_NODELAY");
302
set_socket_options(s2, "TCP_NODELAY");
308
if (write(s2, &c, 1) != 1) {
311
if (read(s1, &c, 1) != 1) {
314
if (w == 1 && (end_timer() > 1.0)) {
315
printf("%8u ops/sec\r",
316
(unsigned)(2*count/end_timer()));
325
int main(int argc, char *argv[])
327
worker(argv[1], atoi(argv[2]), atoi(argv[3]), 1);