1、ttcp作用:检测TCP吞吐量
测试的数据是每秒传输的字节数
带宽 Mb/s
测试程序的性能指标: 传输带宽,QPS/TPS, 以及 CPU利用率,延迟等等。
2、ttcp应用层协议:
3. 尝试自己用C语言写出简单的TTCP程序
先发送一个SessionMessage包中number表示消息的条数,length表示每条消息的长度
PayloadMessage包中存放数据的长度和数据
服务端接收函数
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)
{
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);
}
先看传入的对象,封装了端口等信息
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)
{
}
};
接收一个TCP连接
int sockfd = acceptOrDie(opt.port);
处理第一个包
这个结构体就是发送的消息数,和每条消息的长度,收到数据后,要转换一下字节序
struct SessionMessage
{
int32_t number;
int32_t length;
} __attribute__ ((__packed__));
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);
看看read_n 函数,读n个字节数据
static 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; // EOF
}
else if (errno != EINTR)
{
perror("read");
break;
}
}
return nread;
}
读完第一个包信息后,生成第二个包的结构体
const int total_len = static_cast<int>(sizeof(int32_t) + sessionMessage.length);
PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));
这里使用了static_cast代替C风格的强转
然后读取多条数据
先看数据包结构体的定义
struct PayloadMessage
{
int32_t length;
char data[0];
};
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);
发送一条确认消息函数,完整写n个字节
static int write_n(int sockfd, const void* buf, int 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;
}
客户端代码
客户端的逻辑就是连接客户端,按照规定的应用层协议发送数据
void transmit(const Options& opt)
{
InetAddress addr(opt.port);
if (!InetAddress::resolve(opt.host.c_str(), &addr))
{
printf("Unable to resolve %s\n", opt.host.c_str());
return;
}
printf("connecting to %s\n", addr.toIpPort().c_str());
TcpStreamPtr stream(TcpStream::connect(addr));
if (!stream)
{
printf("Unable to connect %s\n", addr.toIpPort().c_str());
perror("");
return;
}
if (opt.nodelay)
{
stream->setTcpNoDelay(true);
}
printf("connected\n");
double start = now();
struct SessionMessage sessionMessage = { 0, 0 };
sessionMessage.number = htonl(opt.number);
sessionMessage.length = htonl(opt.length);
if (stream->sendAll(&sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))
{
perror("write SessionMessage");
return;
}
const int total_len = sizeof(int32_t) + opt.length;
PayloadMessage* payload = static_cast<PayloadMessage*>(::malloc(total_len));
std::unique_ptr<PayloadMessage, void (*)(void*)> freeIt(payload, ::free);
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 = stream->sendAll(payload, total_len);
assert(nw == total_len);
int ack = 0;
int nr = stream->receiveAll(&ack, sizeof(ack));
assert(nr == sizeof(ack));
ack = ntohl(ack);
assert(ack == opt.length);
}
double elapsed = now() - start;
printf("%.3f seconds\n%.3f MiB/s\n", elapsed, total_mb / elapsed);
}