1/*
2 * Copyright (c) 2014, University of Washington.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich.
8 * Attn: Systems Group.
9 */
10
11#include <stdio.h>
12#include <unistd.h>
13#include <stdlib.h>
14#include <string.h>
15#include <netdb.h>
16#include <inttypes.h>
17#include <sys/types.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21#include <sys/time.h>
22#include <strings.h>
23#include <assert.h>
24#include <pthread.h>
25
26#define	timersub(a, b, result)						      \
27  do {									      \
28    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;			      \
29    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;			      \
30    if ((result)->tv_usec < 0) {					      \
31      --(result)->tv_sec;						      \
32      (result)->tv_usec += 1000000;					      \
33    }									      \
34  } while (0)
35
36#define BUFSIZE         1024
37#define MAX_ROUNDS      10000000
38
39#ifndef MIN
40#define MIN(a,b)        ((a) < (b) ? (a) : (b))
41#endif
42#ifndef MAX
43#define MAX(a,b)        ((a) > (b) ? (a) : (b))
44#endif
45
46static struct timeval tvs[MAX_ROUNDS], tst[MAX_ROUNDS];
47
48/*
49 * error - wrapper for perror
50 */
51static void error(char *msg) {
52  perror(msg);
53  exit(1);
54}
55
56static int sockfd; /* socket */
57static struct sockaddr_in clientaddr; /* client addr */
58static socklen_t clientlen; /* byte size of client's address */
59static int delay = 0;
60static size_t rounds, packets = 0;
61
62static void *receiver_func(void *unused)
63{
64  char buf[BUFSIZE]; /* message buf */
65
66  for(int i = 0; i < rounds; i++) {
67    /*
68     * recvfrom: receive a UDP datagram from a client
69     */
70      int n = recvfrom(sockfd, buf, BUFSIZE, 0, NULL, NULL);
71      if (n < 0)
72          error("ERROR in recvfrom");
73
74      /* printf("server received %d bytes\n", n); */
75      assert(n == BUFSIZE);
76
77      /* uint64_t *cnt = (uint64_t *)buf; */
78      struct timeval *tstamp = (struct timeval *)&buf[8];
79      gettimeofday(&tst[i], NULL);
80      timersub(&tst[i], tstamp, &tvs[i]);
81      packets++;
82      /* if(*cnt != i) { */
83      /*     printf("Packets reordered? %d != %" PRIu64 "\n", i, *cnt); */
84      /*     exit(1); */
85      /* } */
86  }
87
88  return NULL;
89}
90
91int main(int argc, char **argv) {
92  int portno; /* port to listen on */
93  int optval; /* flag value for setsockopt */
94  char buf[BUFSIZE]; /* message buf */
95
96  /*
97   * check command line arguments
98   */
99  if (argc < 6) {
100    fprintf(stderr, "usage: %s <port> <server IP> <delay us> <rounds> <start_val>\n", argv[0]);
101    exit(1);
102  }
103  portno = atoi(argv[1]);
104  delay = atoi(argv[3]);
105  rounds = atoi(argv[4]);
106  assert(rounds < MAX_ROUNDS);
107  size_t start = atoi(argv[5]);
108
109  /*
110   * socket: create the parent socket
111   */
112  sockfd = socket(AF_INET, SOCK_DGRAM, 0);
113  if (sockfd < 0)
114    error("ERROR opening socket");
115
116  /* setsockopt: Handy debugging trick that lets
117   * us rerun the server immediately after we kill it;
118   * otherwise we have to wait about 20 secs.
119   * Eliminates "ERROR on binding: Address already in use" error.
120   */
121  optval = 1;
122  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
123             (const void *)&optval , sizeof(int));
124
125  clientlen = sizeof(clientaddr);
126
127  bzero((char *) &clientaddr, sizeof(clientaddr));
128  clientaddr.sin_family = AF_INET;
129  clientaddr.sin_addr.s_addr = inet_addr(argv[2]);
130  if(clientaddr.sin_addr.s_addr == INADDR_NONE) {
131      printf("Error on inet_addr()\n");
132      exit(1);
133  }
134  clientaddr.sin_port = htons((unsigned short)portno);
135
136  pthread_t sender;
137  int ret = pthread_create(&sender, NULL, receiver_func, NULL);
138  assert(ret == 0);
139
140  uint64_t *cnt = (uint64_t *)buf;
141  struct timeval *tstamp = (struct timeval *)&buf[8];
142
143  memset(tstamp, 0, sizeof(struct timeval));
144
145  for(*cnt = 0; *cnt < rounds; (*cnt)++) {
146      struct timeval oldstamp, diff;
147
148      oldstamp = *tstamp;
149
150      do {
151          gettimeofday(tstamp, NULL);
152          timersub(tstamp, &oldstamp, &diff);
153          /* printf("now = %lu, oldstamp = %lu, diff = %lu, delay = %d\n", */
154          /*        tstamp->tv_sec * 1000000 + tstamp->tv_usec, */
155          /*        oldstamp.tv_sec * 1000000 + oldstamp.tv_usec, */
156          /*        diff.tv_sec * 1000000 + diff.tv_usec, delay); */
157      } while((uint64_t)(diff.tv_sec * 1000000 + diff.tv_usec) < (uint64_t)delay);
158
159      int n = sendto(sockfd, buf, BUFSIZE, 0,
160                     (struct sockaddr *) &clientaddr, clientlen);
161      if (n < 0)
162          error("ERROR in sendto");
163  }
164
165  ret = pthread_cancel(sender);
166  assert(ret == 0);
167
168  unsigned long sum = 0, min = 99999, max = 0;
169
170  for(int i = 0; i < packets; i++) {
171      unsigned long r = tvs[i].tv_sec * 1000000 + tvs[i].tv_usec;
172      unsigned long t = tst[i].tv_sec * 1000000 + tst[i].tv_usec;
173      if(t != 0) {
174          printf("%zd %lu %lu %lu us\n", i + start, t - r, t, r);
175          sum += r;
176          min = MIN(min, r);
177          max = MAX(max, r);
178      }
179  }
180
181  printf("average %lu us, min %lu us, max %lu us\n",
182         sum / (packets - 1), min, max);
183
184  return 0;
185}
186