191852b37SShuo Chen#include "InetAddress.h"
291852b37SShuo Chen#include "TcpStream.h"
391852b37SShuo Chen
4be081f78SShuo Chen#include <limits>
591852b37SShuo Chen#include <memory>
691852b37SShuo Chen#include <unistd.h>
791852b37SShuo Chen#include <stdlib.h>
891852b37SShuo Chen#include <stdio.h>
991852b37SShuo Chen#include <time.h>
1091852b37SShuo Chen
1191852b37SShuo Chen#include <vector>
1291852b37SShuo Chen
1391852b37SShuo Chenint64_t kNanos = 1e9;
1491852b37SShuo Chenint64_t clock_diff(struct timespec x, struct timespec y)
1591852b37SShuo Chen{
1691852b37SShuo Chen  return (x.tv_sec - y.tv_sec) * kNanos + x.tv_nsec - y.tv_nsec;
1791852b37SShuo Chen}
1891852b37SShuo Chen
1991852b37SShuo Chenstruct timespec get_time()
2091852b37SShuo Chen{
2191852b37SShuo Chen  struct timespec now;
2291852b37SShuo Chen  clock_gettime(CLOCK_MONOTONIC, &now);
2391852b37SShuo Chen  return now;
2491852b37SShuo Chen}
2591852b37SShuo Chen
2691852b37SShuo Chenstruct Sample
2791852b37SShuo Chen{
2891852b37SShuo Chen  int index;
2991852b37SShuo Chen  int64_t start_nano;
3091852b37SShuo Chen  int64_t rtt_nano;
3191852b37SShuo Chen};
3291852b37SShuo Chen
33be081f78SShuo Chenstd::vector<Sample> run(const char* host, int delay_ms, int length_s, int batch, int payload_size, bool silent)
3491852b37SShuo Chen{
35be081f78SShuo Chen  const struct timespec start = get_time();
3691852b37SShuo Chen  std::vector<Sample> rtts;
3791852b37SShuo Chen
3891852b37SShuo Chen  InetAddress addr;
3991852b37SShuo Chen  if (!InetAddress::resolve(host, 3007, &addr))
4091852b37SShuo Chen  {
4191852b37SShuo Chen    printf("Unable to resolve %s\n", host);
4291852b37SShuo Chen    return rtts;
4391852b37SShuo Chen  }
4491852b37SShuo Chen
4591852b37SShuo Chen  // printf("connecting to %s\n", addr.toIpPort().c_str());
4691852b37SShuo Chen  TcpStreamPtr stream(TcpStream::connect(addr));
4791852b37SShuo Chen  if (!stream)
4891852b37SShuo Chen  {
4991852b37SShuo Chen    printf("Unable to connect %s\n", addr.toIpPort().c_str());
5091852b37SShuo Chen    perror("");
5191852b37SShuo Chen    return rtts;
5291852b37SShuo Chen  }
5391852b37SShuo Chen
5491852b37SShuo Chen  std::unique_ptr<char[]> payload(new char[payload_size]);
55be081f78SShuo Chen  int64_t count = 0, sum_rtt = 0;
56be081f78SShuo Chen  int64_t min_rtt = std::numeric_limits<int64_t>::max(), max_rtt = 0;
5791852b37SShuo Chen
5891852b37SShuo Chen  while (true) {
59be081f78SShuo Chen    const struct timespec batch_start = get_time();
60be081f78SShuo Chen    double elapsed_s = (double)clock_diff(batch_start, start) / kNanos;
61be081f78SShuo Chen    if (elapsed_s >= length_s) {
62be081f78SShuo Chen      if (silent && count > 0) {
63be081f78SShuo Chen        printf("count %ld, avg rtt %.2fus, min %.2fus, max %.2fus\n",
64be081f78SShuo Chen               count, sum_rtt / 1e3 / count, min_rtt / 1e3, max_rtt / 1e3);
65be081f78SShuo Chen      }
6691852b37SShuo Chen      break;
67be081f78SShuo Chen    }
6891852b37SShuo Chen
6991852b37SShuo Chen    for (int i = 0; i < batch; ++i) {
70be081f78SShuo Chen      const struct timespec before = get_time();
7191852b37SShuo Chen      int nw = stream->sendAll(payload.get(), payload_size);
7291852b37SShuo Chen      if (nw != payload_size)
7391852b37SShuo Chen        return rtts;
7491852b37SShuo Chen      int nr = stream->receiveAll(payload.get(), payload_size);
7591852b37SShuo Chen      if (nr != payload_size)
7691852b37SShuo Chen        return rtts;
77be081f78SShuo Chen      const struct timespec after = get_time();
78be081f78SShuo Chen      int64_t rtt = clock_diff(after, before);
79be081f78SShuo Chen      ++count;
80be081f78SShuo Chen      sum_rtt += rtt;
81be081f78SShuo Chen      if (rtt > max_rtt)
82be081f78SShuo Chen        max_rtt = rtt;
83be081f78SShuo Chen      if (rtt < min_rtt)
84be081f78SShuo Chen        min_rtt = rtt;
85be081f78SShuo Chen
8691852b37SShuo Chen      Sample s = {
8791852b37SShuo Chen        .index = i,
88be081f78SShuo Chen        .rtt_nano = rtt,
8991852b37SShuo Chen      };
9091852b37SShuo Chen      if (i == 0)
9191852b37SShuo Chen        s.start_nano = clock_diff(before, start);
9291852b37SShuo Chen      else
93be081f78SShuo Chen        s.start_nano = clock_diff(before, batch_start);
94be081f78SShuo Chen      if (!silent)
95be081f78SShuo Chen        rtts.push_back(s);
9691852b37SShuo Chen    }
9791852b37SShuo Chen
9891852b37SShuo Chen    if (delay_ms > 0) {
9991852b37SShuo Chen      ::usleep(delay_ms * 1000);
10091852b37SShuo Chen    }
10191852b37SShuo Chen  }
10291852b37SShuo Chen  return rtts;
10391852b37SShuo Chen}
10491852b37SShuo Chen
10591852b37SShuo Chenint main(int argc, char* argv[])
10691852b37SShuo Chen{
10791852b37SShuo Chen  int opt;
10891852b37SShuo Chen  int delay = 0, length = 3, batch = 4, payload = 1;
10991852b37SShuo Chen  bool silent = false;
11091852b37SShuo Chen  while ((opt = getopt(argc, argv, "b:d:l:p:s")) != -1) {
11191852b37SShuo Chen    switch (opt) {
11291852b37SShuo Chen      case 'b':
11391852b37SShuo Chen        batch = atoi(optarg);
11491852b37SShuo Chen        break;
11591852b37SShuo Chen      case 'd':
11691852b37SShuo Chen        delay = atoi(optarg);
11791852b37SShuo Chen        break;
11891852b37SShuo Chen      case 'l':
11991852b37SShuo Chen        length = atoi(optarg);
12091852b37SShuo Chen        break;
12191852b37SShuo Chen      case 'p':
12291852b37SShuo Chen        payload = atoi(optarg);
12391852b37SShuo Chen        break;
12491852b37SShuo Chen      case 's':
12591852b37SShuo Chen        silent = true;
12691852b37SShuo Chen        break;
12791852b37SShuo Chen      default:
12891852b37SShuo Chen        ;
12991852b37SShuo Chen    }
13091852b37SShuo Chen  }
13191852b37SShuo Chen  if (optind >= argc) {
13291852b37SShuo Chen    fprintf(stderr, "Usage:\nroundtrip_tcp [-b batch_size] [-d delay_ms] [-l length_in_seconds] echo_server_host\n");
13391852b37SShuo Chen    return 1;
13491852b37SShuo Chen  }
135be081f78SShuo Chen  if (batch < 1) {
136be081f78SShuo Chen    batch = 1;
137be081f78SShuo Chen  }
138be081f78SShuo Chen  if (delay < 0) {
139be081f78SShuo Chen    delay = 0;
140be081f78SShuo Chen  }
141be081f78SShuo Chen  if (payload < 1) {
142be081f78SShuo Chen    payload = 1;
143be081f78SShuo Chen  }
144be081f78SShuo Chen
145be081f78SShuo Chen  std::vector<Sample> rtts = run(argv[optind], delay, length, batch, payload, silent);
14691852b37SShuo Chen  if (!silent) {
147be081f78SShuo Chen    printf("index start rtt\n");
14891852b37SShuo Chen    for (Sample s : rtts) {
14991852b37SShuo Chen      printf("%d %ld %ld\n", s.index, s.start_nano, s.rtt_nano);
15091852b37SShuo Chen    }
15191852b37SShuo Chen  }
15291852b37SShuo Chen}
153