C++后台开发 网络编程实践一

TTCP(test TCP): classic TCP performance testing tool

1. What performance do we care?
        1. Bandwith MB/s
        2. Throughput, messages/s, queries/s(QPS), transactions/s (TPS)
        3. Latency, millisecond, percentiles
        4. Utilization, percent, payload vs. carrier, goodput vs. theory BW
        5. Overhead, eg. CPU usage, for compression and /or encryption
2. Why do we re-implement TTCP?
    It uses all basic Sockets APIs: socket, listen, bind, accept, connect, read/recv, write/send, shutdown, close, etc.
    The protocol is binary, not just byte stream, so it's better than the classic echo example
    Typical behaviors, meaningful result, instead of packet/s 
    Service as benchmark for programming language as well, by comparing CPU usage
    Not concurrent, at least in the very basic form
3. The Protocol

C++后台开发 网络编程实践一_第1张图片
4. The Code
C++后台开发 网络编程实践一_第2张图片
5. 这个例子用到了muduo库的Timestamp.h来计算时间,需要编译一下muduo库。具体方法:
6. 实例代码:

//main.cc
#include "commandLineParser.h"
#include "clientAndServer.h"

#include <assert.h>

int main(int argc, char *argv[]) {
  Options options;
  parseCommandLine(argc, argv, &options);
  if(options.transmit){
    transmit(options);
  }
  else if(options.receive){
    receive(options);
  }
  else{
    assert(0);
  }
  return 0;
}
//commandLineParser.h
#pragma once

#include 
#include 

//需要从命令行获取的信息
struct Options{
  uint16_t port;
  int length;
  int number;
  bool transmit, receive, nodelay;
  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 resolveAddr(const char * host, uint16_t port);
//commandLineParser.cc
#include "commandLineParser.h"

#include 

#include 

#include 
#include 

using namespace boost::program_options;

bool parseCommandLine(int argc, char *argv[], Options *opt){
  options_description desc("Allowed options");
  desc.add_options()
    ("help,h", "Help")
    ("port,p", value(&opt->port)->default_value(5001), "TCP port")
    ("length,l", value<int>(&opt->length)->default_value(8192), "Buffer length")
    ("number,n", value<int>(&opt->number)->default_value(1025), "Number of buffers")
    ("trans,t", value<std::string>(&opt->host), "Transmit")
    ("recv,r", "Receive")
    ("nodelay,D", "set TCP_NODELAY")
    ;
  variables_map vm;
  store(parse_command_line(argc, argv, desc), vm);
  notify(vm);

  opt->transmit = vm.count("trans");
  opt->receive = vm.count("recv");
  opt->nodelay = vm.count("nodelay");

  if(vm.count("help")){
    std::cout << desc << std::endl;
    return false;
  }
  if(opt->transmit == opt->receive){
    printf("either -t or -r must be specified.\n");
    return false;
  }

  printf("port = %d\n", opt->port);

  if(opt->transmit){
    printf("buffer length = %d\n", opt->length);
    printf("number of buffers = %d\n", opt->number);
  }
  else{
    printf("accepting...\n");
  }
  return true;
}
struct sockaddr_in resolveAddr(const char * host, uint16_t port){
  //struct hostent *gethostbyname(const char *hostname);
  /*struct hostent{
      char * h_name;
      char ** h_aliases;
      int h_addrtype;
      int h_length;
      char ** h_addr_list;
      #define h_addr h_addr_list[0]
*/
  struct hostent * getHost = gethostbyname(host);
  if(!getHost){
    printf(hstrerror(h_errno));
    exit(1);
  }
  assert(getHost->h_addrtype == AF_INET && getHost->h_length == sizeof(uint32_t));
  struct sockaddr_in addr;
  bzero(&addr, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  //完全无关类型转换 (char *) to (struct in_addr *)
  addr.sin_addr = *reinterpret_cast<struct in_addr*>(getHost->h_addr);
  return addr;
}
//clientAndServer.h
#pragma once

#include "commandLineParser.h"
#include "protocol.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
// write n byte to sockfd
int write_n(int sockfd, const void*buf, int length);
// read n byte from sockfd
int read_n(int sockfd, void *buf, int length);
// accept client
int acceptCli(uint16_t port);
// transmit SessionMessage or playload to server
void transmit(const Options &opt);
// receive SessionMessage or playload from client
void receive(const Options & opt);
//clientAndServer.cc
#include "commandLineParser.h"
#include "clientAndServer.h"

int acceptCli(uint16_t port){
  int listenfd = socket(AF_INET, SOCK_STREAM, 0);
  assert(listenfd >= 0);
  int yes = 1;
  //int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
  if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))){
    perror("setsockopt");
    exit(1);
  }
  struct sockaddr_in servaddr;
  bzero(&servaddr, sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_port = htons(port);
  servaddr.sin_addr.s_addr = INADDR_ANY;
  if(bind(listenfd, reinterpret_cast<struct sockaddr *>(&servaddr), sizeof(servaddr))){
    perror("bind");
    exit(1);
  }
  if(listen(listenfd, 5)){
    perror("listen");
    exit(1);
  }
  int sockfd = accept(listenfd, reinterpret_cast<struct sockaddr *>(NULL), NULL);
  if(sockfd < 0){
    perror("accept");
    exit(1);
  }
  close(listenfd);
  return sockfd;
}

int read_n(int sockfd, void *buf, int length){
  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;
    }
    else if(errno != EINTR){
      perror("read");
      break;
    }
  }
  return nread;
}

int write_n(int sockfd, const void * buf, int length){
  int nwrite = 0;
  while(nwrite < length){
    ssize_t nw = write(sockfd, static_cast<const char *>(buf) + nwrite, length - nwrite);
    if(nw > 0){
      nwrite += static_cast<int>(nw);
    }
    else if(nw == 0){
      break; //EOF
    }
    else if(errno != EINTR){
      perror("read");
      break;
    }
  }
  return nwrite;
}

void transmit(const Options & opt){
  //建立连接
  struct sockaddr_in addr = resolveAddr(opt.host.c_str(), opt.port);
  printf("connecting to %s:%d\n", inet_ntoa(addr.sin_addr), opt.port);

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

  printf("connected\n");
  // start timing
  muduo::Timestamp start(muduo::Timestamp::now());
  // construct sessionMessage
  struct SessionMessage sessionMessage = {0, 0};
  sessionMessage.number = ntohl(opt.number);
  sessionMessage.length = ntohl(opt.length);
  // send sessionMessage
  if(write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage)){
    perror("write SessionMessage");
    exit(1);
  }
  // construct payload
  const int total_len = static_cast<int>(sizeof(int32_t) + opt.length);
  PayloadMessage * payload = static_cast(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);
  // send PayloadMessage
  for(int i = 0; i < opt.number; i++){
    int nw = write_n(sockfd, payload, total_len);
    assert(nw == total_len);

    int ack = 0;
    //god job! the ack package is 4 byte, sizeof(int)!;
    int nr = read_n(sockfd, &ack, sizeof(ack));
    assert(nr == sizeof(ack));
    ack = ntohl(ack);
    assert(ack == opt.length);
  }
  //释放内存
  free(payload);
  //关闭sock连接
  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 = acceptCli(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\n receive length = %d\n",
          sessionMessage.number, sessionMessage.length);
  const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage.length);
  PayloadMessage * payload = static_cast(malloc(total_len));
  assert(payload);

  //接收payload
  for(int i = 0; i < sessionMessage.number; i++){
    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);
}
//protocol.h
#pragma once
#include 

struct SessionMessage{
  int32_t number;
  int32_t length;
};

struct PayloadMessage{
  int32_t length;
  //usage: PayloadMessage * payload = static_cast(::malloc(total_len));
  char data[0];
};
//makefile 这个makefile的库我采用了相对路径,仅供参考。
test:commandLineParser.o clientAndServer.o main.o
    g++ commandLineParser.o clientAndServer.o main.o -o test -L../../../build/release-install/lib -lmuduo_net -lmuduo_base -lboost_program_options

commandLineParser.o:commandLineParser.h commandLineParser.cc
    g++ -c commandLineParser.cc -o commandLineParser.o

clientAndServer.o:clientAndServer.cc clientAndServer.h
    g++ -c clientAndServer.cc -o clientAndServer.o -I../../../build/release-install/include
main.o:
    g++ -c main.cc -o main.o -I../../../build/release-install/include

clean:
    rm -rf *.o test
//用wc -l *.h *.cc 命令算出总共318行代码

//性能测试后续给出
未完待续…

你可能感兴趣的:(C++后台开发 网络编程实践一)