简单的TCP带宽测试工具TTCP

源码可以从陈硕的github上下载到,位置在muduo-master\examples\ace\ttcp

TTCP是一个传统的测试TCP性能的工具,它主要测试两个机器之间TCP的吞吐量,在应用层模拟消息传递的过程——客户端发送一个包,服务端对包进行确认,并且在确认之前客户端不发送下一个包。可以设置发包的数量和每个包的大小,程序计算传输所有包所用的时间,然后计算出带宽。

操作流程图

以上发送的message都是在应用层中体现的。当一个client发起ttcp的请求时,首先会向server发送一个SessionMessage,里边主要包含了即将发送的数据包个数和每个包的长度。PayloadMessage便是实际的数据,里边包含了一个表示长度的length,和实际的数据data,其中data使用了柔性数组。

main.cc

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

#include <assert.h>

int main(int argc, char* argv[])
{
  Options options;								//用来保存输入的参数信息
  if (parseCommandLine(argc, argv, &options))	//把命令行输入的参数保存到结构体中
  {
    if (options.transmit)						//让发送端和接收端分别执行对应的函数
    {
      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;					//端口
  int length;						//报文长度
  int number;						//报文数量
  bool transmit, receive, nodelay;	//发送端,接收端,是否在传输层禁用negla算法
  std::string host;					//主机名
  Options()
    : port(0), length(0), number(0),
      transmit(false), receive(false), nodelay(false)
  {
  }
};

bool parseCommandLine(int argc, char* argv[], Options* opt);		//解析命令
struct sockaddr_in resolveOrDie(const char* host, uint16_t port);	//通过域名和端口解析出服务端的sockaddr

struct SessionMessage				//本次请求的信息
{
  int32_t number;					//报文数量
  int32_t length;					//报文长度
} __attribute__ ((__packed__));

struct PayloadMessage				//消息报文
{
  int32_t length;					//长度
  char data[0];						//数据
};

void transmit(const Options& opt);	//发送端

void receive(const Options& opt);	//接收端

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)									//一次只服务一个客户端,创建一个listenfd收到连接就close,返回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)			//写length个字符,返回写成功的字符个数
{
  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)				//读legnth个字符,返回读到的字符个数
{
  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)														//发送端执行
{
  struct sockaddr_in addr = resolveOrDie(opt.host.c_str(), opt.port);					//输入域名端口,返回表示server的sockaddr
  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));	//连接服务端
  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());										//记录当前时间
  struct SessionMessage sessionMessage = { 0, 0 };										//填写信息报文
  sessionMessage.number = htonl(opt.number);
  sessionMessage.length = htonl(opt.length);
  if (write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))//如果没有全部发送直接退出
  {
    perror("write SessionMessage");
    exit(1);
  }

  const int total_len = static_cast<int>(sizeof(int32_t) + opt.length);					//计算数据报文长度
  PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));
  assert(payload);
  payload->length = htonl(opt.length);
  for (int i = 0; i < opt.length; ++i)													//填充数据
  {
    payload->data[i] = "0123456789ABCDEF"[i % 16];
  }

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

  for (int i = 0; i < opt.number; ++i)													//发收数据
  {
    int nw = write_n(sockfd, payload, total_len);
    assert(nw == total_len);

    int ack = 0;
    int nr = read_n(sockfd, &ack, sizeof(ack));											//每个包发出之后要等收到对应的确认再发下一个
    assert(nr == sizeof(ack));
    ack = ntohl(ack);
    assert(ack == opt.length);
  }

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

void receive(const Options& opt)
{
  int sockfd = acceptOrDie(opt.port);													//接受客户端连接

  struct SessionMessage sessionMessage = { 0, 0 };
  if (read_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))//读消息报文
  {
    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));			//申请对应大小的缓冲区
  assert(payload);

  for (int i = 0; i < sessionMessage.number; ++i)										//读数据发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);
}

猜你喜欢

转载自blog.csdn.net/qq_33113661/article/details/89919609