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); }