协议
代码
client代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <error.h>
#include <strings.h>
#include <sys/types.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <memory>
#include <iostream>
#include <sys/time.h>
struct SessisonMessage
{
int32_t number;
int32_t length;
}__attribute__ ((__packed__));
struct PayloadMessage
{
int32_t length;
char data[0];
};
double now()
{
struct timeval tv = { 0, 0 };
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
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;
}
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;
}
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf(stderr, "./uasge");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
const char *ip = argv[1];
inet_aton(ip, &(addr.sin_addr));
addr.sin_port = htons(12345);
int sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
size_t len = sizeof(addr);
int ret = ::connect(sockfd, (struct sockaddr*)&addr, len);
if (ret)
{
perror("connect");
close(sockfd);
exit(1);
}
printf("connected\n");
// 65536 bytes per message
int length = 65536;
int number = 8192;
if (argc == 3)
{
length = atoi(argv[2]);
}
if (argc == 4)
{
length = atoi(argv[2]);
number = atoi(argv[3]);
}
double start = now();
SessisonMessage sessionMessage = {0, 0};
sessionMessage.length = htonl(length);
sessionMessage.number = htonl(number);
if (write_n(sockfd, &sessionMessage, sizeof(sessionMessage)) != sizeof(sessionMessage))
{
perror("write sessionMessage");
exit(1);
}
//
const int total_len = static_cast<int>(sizeof(int32_t) + length);
PayloadMessage *payloadMessage = static_cast<PayloadMessage*>(::malloc(total_len));
assert(payloadMessage);
//fill in payloadMessage
payloadMessage->length = htonl(length);
for (int i=0; i<length; ++i)
payloadMessage->data[i] = "0123456789ABCDEF"[i % 16];
double total_mb = 1.0 * length * number / 1024 / 1024;
printf("%.3f in MiB in total\n", total_mb);
for (int i=0; i<number; ++i)
{
//send length
if (write_n(sockfd, payloadMessage, total_len) != total_len)
{
perror("write payload");
exit(1);
}
int32_t ack;
if (read_n(sockfd, &ack, sizeof(ack)) != sizeof(ack))
{
perror("read");
exit(1);
}
ack = ntohl(ack);
assert(ack == length);
}
::free(payloadMessage);
::close(sockfd);
double elapsed = now() - start;
printf("%.3f seconds\n%.3f MiB/s\n", elapsed, total_mb / elapsed);
return 1;
}
server端代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <error.h>
#include <strings.h>
#include <sys/types.h>
#include <pthread.h>
#include <fcntl.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#include <memory>
#include <iostream>
#include <sys/time.h>
struct SessisonMessage
{
int32_t number;
int32_t length;
}__attribute__ ((__packed__));
struct PayloadMessage
{
int32_t length;
char data[0];
};
static double now()
{
struct timeval tv = { 0, 0 };
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
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;
}
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;
}
static int acceptOrDie(uint16_t port)
{
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;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
// addr.sin_addr.s_addr = INADDR_ANY;
const char *ip = "192.168.100.101";
inet_aton(ip, &(addr.sin_addr));
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;
bzero(&peer_addr, sizeof(peer_addr));
socklen_t addrlen = 0;
//reinterpret_cast
int sockfd = ::accept(listenfd, reinterpret_cast<struct sockaddr*>(&peer_addr), &addrlen);
if (sockfd < 0)
{
perror("accept");
exit(1);
}
::close(listenfd);
return sockfd;
}
int main(int argc, char **argv)
{
int sockfd = acceptOrDie(12345);
SessisonMessage sessisonMessage = {0,0};
if (read_n(sockfd, &sessisonMessage, sizeof(sessisonMessage)) != sizeof(sessisonMessage))
{
perror("readn message");
return 1;
}
sessisonMessage.length = ntohl(sessisonMessage.length);
sessisonMessage.number = ntohl(sessisonMessage.number);
printf("receive number = %d, receive length = %d\n", sessisonMessage.number, sessisonMessage.length);
const int total_len = static_cast<int> (sizeof(int32_t) + sessisonMessage.length);
PayloadMessage *payloadMessage = static_cast<PayloadMessage*>(::malloc(total_len));
assert(payloadMessage);
for (int i=0; i<sessisonMessage.number; ++i)
{
if (read_n(sockfd, &(payloadMessage->length), sizeof(payloadMessage->length)) != sizeof(payloadMessage->length))
{
perror("read length of payload");
return 1;
}
payloadMessage->length = ntohl(payloadMessage->length);
// cout << "payloadMessage->length" << payloadMessage->length << endl;
// printf("payloadMessage->length is %d\n", payloadMessage->length);
assert(payloadMessage->length == sessisonMessage.length);
if (read_n(sockfd, payloadMessage->data, payloadMessage->length) != payloadMessage->length)
{
perror("read payload");
exit(1);
}
int32_t ack = htonl(payloadMessage->length);
if (write_n(sockfd, &ack, sizeof(ack)) != sizeof(ack))
{
perror("write ack");
exit(1);
}
}
::free(payloadMessage);
::close(sockfd);
}
代码参照陈硕老师写的,写的过程中注意主机字节序和网络字节序的转换。
测试
测试1
服务端
[root@bogon bin]# while true; do ./Ser ; done
客户端
[root@bogon bin]# ./Cli ip(x.x.x.x)
试下2048,4096,8192,16384,32768,65536,128000(128k),256000(256k的时候可以)得到结果
应用层发消息一般没有这么大(几百k的数据)
TCP慢启动的影响(运行时间也不能太短)
这个可以看出TTCP测的带宽与消息的大小有关系,消息越小,传输延迟的影响作用就越大,
测试1的结果,基本上可以达到千兆网的带宽。
测试2
cli端和ser端都在本机上,测得的结果,可以达到up to 3GB/s的带宽,说明TCP/IP通信用在本机进程间通信的话,带宽也是很不错的。