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

来源:互联网 发布:java nexttoken 编辑:程序博客网 时间:2024/06/14 14:14

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 encryption2. 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 form3. The Protocol

这里写图片描述
4. The Code
这里写图片描述
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 <string>#include <stdint.h>//需要从命令行获取的信息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 <boost/program_options.hpp>#include <iostream>#include <netdb.h>#include <stdio.h>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<uint16_t>(&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 <stdint.h>#include <assert.h>#include <errno.h>#include <stdio.h>#include <sys/socket.h>#include <sys/types.h>#include <string.h>#include <malloc.h>#include <netinet/in.h>#include <arpa/inet.h>#include <muduo/base/Timestamp.h>// write n byte to sockfdint write_n(int sockfd, const void*buf, int length);// read n byte from sockfdint read_n(int sockfd, void *buf, int length);// accept clientint acceptCli(uint16_t port);// transmit SessionMessage or playload to servervoid transmit(const Options &opt);// receive SessionMessage or playload from clientvoid 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<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);  // 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<PayloadMessage*>(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 <stdint.h>struct SessionMessage{  int32_t number;  int32_t length;};struct PayloadMessage{  int32_t length;  //usage: PayloadMessage * payload = static_cast<PayloadMessage *>(::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_optionscommandLineParser.o:commandLineParser.h commandLineParser.cc    g++ -c commandLineParser.cc -o commandLineParser.oclientAndServer.o:clientAndServer.cc clientAndServer.h    g++ -c clientAndServer.cc -o clientAndServer.o -I../../../build/release-install/includemain.o:    g++ -c main.cc -o main.o -I../../../build/release-install/includeclean:    rm -rf *.o test
//用wc -l *.h *.cc 命令算出总共318行代码

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

3 0