A simple TCP bandwidth testing tool TTCP

The source code can be downloaded from Chen Shuo's github at Muduo master \ examples \ ace \ TTCP

TTCP is a traditional tool to test the performance of TCP. It mainly tests the throughput of TCP between two machines. It simulates the process of message passing in the application layer: the client sends a packet, the server confirms the packet, and the client does not send the next packet before confirming. You can set the number of packets and the size of each packet. The program calculates the time taken to transmit all packets, and then calculates the bandwidth.

Operation flow chart

The messages sent above are all reflected in the application layer. When a client initiates a request for ttcp, it will first send a SessionMessage to the server, which mainly includes the number of packets to be sent and the length of each packet. PayloadMessage is the actual data, which contains a length representing the length and the actual data, in which the data uses a flexible array.

main.cc

#include <examples/ace/ttcp/common.h>

#include <assert.h>

int main(int argc, char* argv[])
{
  Options options;								//Used to save the input parameter information
  if (parseCommandLine(argc, argv, &options))	//Save command line input parameters to the structure
  {
    if (options.transmit)						//Let the sender and receiver execute corresponding functions respectively
    {
      transmit(options);
    }
    else if (options.receive)
    {
      receive(options);
    }
    else
    {
      assert(0);
    }
  }
}

common.h

#pragma once

#include <string>
#include <stdint.h>

struct Options						
{
  uint16_t port;					//port
  int length;						//packet length
  int number;						//Number of packets
  bool transmit, receive, nodelay;	//Whether negla algorithm is disabled at the transmission layer
  std::string host;					//host name
  Options()
    : port(0), length(0), number(0),
      transmit(false), receive(false), nodelay(false)
  {
  }
};

bool parseCommandLine(int argc, char* argv[], Options* opt);		//Analytical command
struct sockaddr_in resolveOrDie(const char* host, uint16_t port);	//Resolving the sockaddr of the server through the domain name and port

struct SessionMessage				//Information of this request
{
  int32_t number;					//Number of packets
  int32_t length;					//packet length
} __attribute__ ((__packed__));

struct PayloadMessage				//message
{
  int32_t length;					//length
  char data[0];						//data
};

void transmit(const Options& opt);	//Sending end

void receive(const Options& opt);	//receiving end

ttcp_blocking.cc

#include <examples/ace/ttcp/common.h>
#include <muduo/base/Timestamp.h>
#include <muduo/base/Types.h>

#undef NDEBUG

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

#include <netinet/in.h>
#include <arpa/inet.h>

static int acceptOrDie(uint16_t port)									//Only serve one client at a time, create a listenfd, close after receiving the connection, and return to clientsock
{
  int listenfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  assert(listenfd >= 0);

  int yes = 1;
  if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
  {
    perror("setsockopt");
    exit(1);
  }

  struct sockaddr_in addr;
  muduo::memZero(&addr, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = INADDR_ANY;
  if (bind(listenfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)))
  {
    perror("bind");
    exit(1);
  }

  if (listen(listenfd, 5))
  {
    perror("listen");
    exit(1);
  }

  struct sockaddr_in peer_addr;
  muduo::memZero(&peer_addr, sizeof(peer_addr));
  socklen_t addrlen = 0;
  int sockfd = ::accept(listenfd, reinterpret_cast<struct sockaddr*>(&peer_addr), &addrlen);
  if (sockfd < 0)
  {
    perror("accept");
    exit(1);
  }
  ::close(listenfd);
  return sockfd;
}

static int write_n(int sockfd, const void* buf, int length)			//Write length characters, return the number of characters written successfully
{
  int written = 0;
  while (written < length)
  {
    ssize_t nw = ::write(sockfd, static_cast<const char*>(buf) + written, length - written);
    if (nw > 0)
    {
      written += static_cast<int>(nw);
    }
    else if (nw == 0)
    {
      break;  // EOF
    }
    else if (errno != EINTR)
    {
      perror("write");
      break;
    }
  }
  return written;
}

static int read_n(int sockfd, void* buf, int length)				//Read legnth characters, return the number of characters read
{
  int nread = 0;
  while (nread < length)
  {
    ssize_t nr = ::read(sockfd, static_cast<char*>(buf) + nread, length - nread);
    if (nr > 0)
    {
      nread += static_cast<int>(nr);
    }
    else if (nr == 0)
    {
      break;  // EOF
    }
    else if (errno != EINTR)
    {
      perror("read");
      break;
    }
  }
  return nread;
}

void transmit(const Options& opt)														//Sender execution
{
  struct sockaddr_in addr = resolveOrDie(opt.host.c_str(), opt.port);					//Enter the domain name port and return the sockaddr representing the server
  printf("connecting to %s:%d\n", inet_ntoa(addr.sin_addr), opt.port);

  int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  assert(sockfd >= 0);
  int ret = ::connect(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));	//Connect to the server
  if (ret)
  {
    perror("connect");
    printf("Unable to connect %s\n", opt.host.c_str());
    ::close(sockfd);
    return;
  }

  printf("connected\n");
  muduo::Timestamp start(muduo::Timestamp::now());										//Record current time
  struct SessionMessage sessionMessage = { 0, 0 };										//Fill in information message
  sessionMessage.number = htonl(opt.number);
  sessionMessage.length = htonl(opt.length);
  if (write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))//Exit without sending all
  {
    perror("write SessionMessage");
    exit(1);
  }

  const int total_len = static_cast<int>(sizeof(int32_t) + opt.length);					//Calculate data message length
  PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));
  assert(payload);
  payload->length = htonl(opt.length);
  for (int i = 0; i < opt.length; ++i)													//Fill data
  {
    payload->data[i] = "0123456789ABCDEF"[i % 16];
  }

  double total_mb = 1.0 * opt.length * opt.number / 1024 / 1024;						//Calculated size
  printf("%.3f MiB in total\n", total_mb);

  for (int i = 0; i < opt.number; ++i)													//Sending and receiving data
  {
    int nw = write_n(sockfd, payload, total_len);
    assert(nw == total_len);

    int ack = 0;
    int nr = read_n(sockfd, &ack, sizeof(ack));											//After each package is sent, the next one will be sent after receiving the corresponding confirmation
    assert(nr == sizeof(ack));
    ack = ntohl(ack);
    assert(ack == opt.length);
  }

  ::free(payload);
  ::close(sockfd);
  double elapsed = timeDifference(muduo::Timestamp::now(), start);						//Output result
  printf("%.3f seconds\n%.3f MiB/s\n", elapsed, total_mb / elapsed);
}

void receive(const Options& opt)
{
  int sockfd = acceptOrDie(opt.port);													//Accept client connection

  struct SessionMessage sessionMessage = { 0, 0 };
  if (read_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))//Read message message
  {
    perror("read SessionMessage");
    exit(1);
  }

  sessionMessage.number = ntohl(sessionMessage.number);
  sessionMessage.length = ntohl(sessionMessage.length);
  printf("receive number = %d\nreceive length = %d\n",
         sessionMessage.number, sessionMessage.length);
  const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage.length);
  PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));			//Request buffer of corresponding size
  assert(payload);

  for (int i = 0; i < sessionMessage.number; ++i)										//Read data send ack
  {
    payload->length = 0;
    if (read_n(sockfd, &payload->length, sizeof(payload->length)) != sizeof(payload->length))
    {
      perror("read length");
      exit(1);
    }
    payload->length = ntohl(payload->length);
    assert(payload->length == sessionMessage.length);
    if (read_n(sockfd, payload->data, payload->length) != payload->length)
    {
      perror("read payload data");
      exit(1);
    }
    int32_t ack = htonl(payload->length);
    if (write_n(sockfd, &ack, sizeof(ack)) != sizeof(ack))
    {
      perror("write ack");
      exit(1);
    }
  }
  ::free(payload);
  ::close(sockfd);
}

 

Keywords: socket github

Added by Restless on Sat, 16 Nov 2019 16:34:32 +0200