c++ - C++ Socket编程:网络低速分析
问题描述
我已经购买了一台 VPS 作为远程服务器,并在该远程服务器上部署了 shadowsocks 作为代理。
情况1:
当我使用 uTorrent 通过 shadowsocks 下载文件时,速度可以达到 10 MB/秒(即 80 Mbits/秒)。示意图是:
--------------------------- -------------------------- ---------
| Local computer | | Remote server | | |
| ------------ | 80 Mbits/sec | ----------- | | |
| uTorrent<-->| shadowsocks||<---------------->| |shadowsocks|<--------------->|Internet |
| | client || | | server | | | |
| ------------ | | ----------- | | |
--------------------------- -------------------------- ---------
案例二:
现在,我用 C++ 编写了一个客户端/服务器程序。但是网速极低,下面是我的测试结果:
客户端:下载速度
t_lRecvTotalBytes = 1024000 字节
t_lRecvTotalTime = 2942410 微秒。
网速:2.7 MBit/s
服务器:上传速度
t_lRecvTotalBytes = 1024000 字节
t_lRecvTotalTime = 1741625 微秒。
网速:4.5 MBit/s
示意图是:
--------------------------- --------------------------
| Local computer | | Remote server |
| --------------- |2.7~4.5 Mbits/sec | -------------- |
| | My C++ client |<------------------------->| My C++ server| |
| --------------- | | -------------- |
| | | |
--------------------------- --------------------------
案例3:
iperf3
用于测试 和 之间的local computer
带宽Remote server
。结果是:
[ID] 间隔传输码率
[ 5] 0.00-10.00 秒 7.25 兆字节 6.08 兆比特/秒
[ 5] 10.00-20.00 秒 11.6 MBytes 9.73 Mbits/sec
[ 5] 20.00-30.00 秒 12.2 兆字节 10.3 兆比特/秒
[ 5] 30.00-40.00 秒 11.4 兆字节 9.53 兆比特/秒
[ 5] 40.00-50.00 秒 12.0 MBytes 10.0 Mbits/sec
[5] 50.00-60.00 秒 8.28 兆字节 6.94 兆比特/秒
[ID] 间隔传输比特率 Retr
[ 5] 0.00-60.00 秒 62.9 MBytes 8.79 Mbits/sec 222 发送方
[ 5] 0.00-60.00 秒 62.7 MBytes 8.77 Mbits/sec 接收器
示意图是:
--------------------------- --------------------------
| Local computer | | Remote server |
| --------------- | 8.8 Mbits/sec | -------------- |
| | iperf3 client |<------------------------->| iperf3 server| |
| --------------- | | -------------- |
| | | |
--------------------------- --------------------------
我的客户/服务器代码
服务器.cpp
#include <iostream>
#include <arpa/inet.h>
#include <string>
#include <chrono>
#include <unistd.h>
#include <stdlib.h>
#include <iomanip>
#include <signal.h>
#include <fcntl.h>
#define PORT (7549)
#define CYCLE_TIME (100)
int g_iSockFd;
void sigQuitHandler(int signal) {
std::cout << "received SIGQUIT, doing graceful shutting down ..."
<< std::endl;
close(g_iSockFd);
exit(1);
}
void sigIntrHandler(int signal) {
std::cout << "received SIGINT, doing graceful shutting down ..."
<< std::endl;
close(g_iSockFd);
exit(1);
}
/*
* FUNCTION : enable or disable blocking of a file descriptor (socket)
* @PARAM : fd file descriptor to be processed
* @PARAM : blocking a bool flag to indicate blocking or non-blocking
* true ----> blocking
* false ----> non-blocking
* @RETURN : true on success
* false if there was an error
*/
bool SetSocketBlockingEnabled(int fd, bool blocking) {
if (fd < 0) {
return false;
}
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
return false;
}
flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
}
int main(int argc, char** argv) {
// register signal handler function
struct sigaction t_struQuitSigAction;
struct sigaction t_struIntrSigAction;
t_struQuitSigAction.sa_handler = sigQuitHandler;
t_struQuitSigAction.sa_flags = 0;
sigemptyset(&t_struQuitSigAction.sa_mask);
sigaction(SIGQUIT, &t_struQuitSigAction, 0);
t_struIntrSigAction.sa_handler = sigIntrHandler;
t_struIntrSigAction.sa_flags = 0;
sigemptyset(&t_struIntrSigAction.sa_mask);
sigaction(SIGINT, &t_struIntrSigAction, 0);
// init socket parameter
char t_cRecvBuffer[102400];
struct sockaddr_in t_struSockAddr;
memset(&t_struSockAddr, 0, sizeof(t_struSockAddr));
int t_iOpt = 1;
// IPv4
t_struSockAddr.sin_family = AF_INET;
t_struSockAddr.sin_addr.s_addr = INADDR_ANY; // inet_addr("127.0.0.1");
t_struSockAddr.sin_port = htons(PORT);
// create socket
int g_iSockFd = socket(AF_INET, SOCK_STREAM, 0);
if (g_iSockFd == -1) {
std::cout << "Error accurs when creating socket."
<< std::endl << "System exits abnormally." << std::endl;
return -1;
}
int t_iError;
if ((t_iError = setsockopt(g_iSockFd, SOL_SOCKET, SO_REUSEADDR,
&t_iOpt, sizeof(t_iOpt))) < 0) {
std::cout << "reuseaddr setsockopt fail. error = "
<< t_iError << std::endl;
return -1;
}
if ((t_iError = setsockopt(g_iSockFd, SOL_SOCKET, SO_REUSEPORT,
&t_iOpt, sizeof(t_iOpt))) < 0) {
std::cout << "reuseport setsockopt fail. error = "
<< t_iError << std::endl;
return -1;
}
SetSocketBlockingEnabled(g_iSockFd, true);
if (bind(g_iSockFd, (struct sockaddr *)&t_struSockAddr,
sizeof(t_struSockAddr)) < 0) {
std::cout << "bind fail." << std::endl;
return -1;
}
if (listen(g_iSockFd, 10) < 0) {
std::cout << "listen fail." << std::endl;
return -1;
}
int t_iNewSocket;
while (1) {
std::string t_strResponse(10240, 0);
struct sockaddr_in t_struClientAddr;
socklen_t t_ClientAddrLen = 0;
memset(&t_struClientAddr, 0, sizeof(t_struClientAddr));
if ((t_iNewSocket = accept(g_iSockFd,
(struct sockaddr*)&t_struClientAddr,
&t_ClientAddrLen)) < 0) {
continue;
}
else {
std::cout << "accept successfully." << std::endl;
}
// recv
long t_lRecvTotalBytes = 0;
long t_lRecvTotalTime = 0;
double t_dSpeed = 0.0;
std::chrono::high_resolution_clock::time_point t_StartTime;
std::chrono::high_resolution_clock::time_point t_EndTime;
bool t_bHasBegin = false;
while (1) {
int t_iRecvBytes = recv(t_iNewSocket, t_cRecvBuffer,
sizeof(t_cRecvBuffer), 0);
if (t_iRecvBytes <= 0) {
std::cout << "Error accurs when receiving. " << std::endl;
break;
}
else {
if (t_bHasBegin == false) {
t_bHasBegin = true;
t_StartTime = std::chrono::high_resolution_clock::now();
}
t_lRecvTotalBytes += t_iRecvBytes;
std::string t_strRecv(t_cRecvBuffer, t_iRecvBytes);
std::cout << "Receiving " << t_iRecvBytes << "/"
<< t_lRecvTotalBytes
<< " bytes. " << std::endl
<< "Msg ends with: "
<< int(t_strRecv[t_iRecvBytes - 1])
<< std::endl;
if (t_cRecvBuffer[t_iRecvBytes - 1] == 1) {
std::cout << "recv finished." << std::endl;
break;
}
}
}
t_EndTime = std::chrono::high_resolution_clock::now();
t_lRecvTotalTime =
std::chrono::duration_cast<std::chrono::microseconds>(
t_EndTime - t_StartTime
).count();
t_dSpeed = double(t_lRecvTotalBytes) / t_lRecvTotalTime
* 1000000 * 8 / 1024 / 1024;
std::cout << "t_lRecvTotalBytes = "
<< t_lRecvTotalBytes << " Bytes" << std::endl;
std::cout << "t_lRecvTotalTime = "
<< t_lRecvTotalTime << " microseconds." << std::endl;
std::cout << "network speed: "
<< std::setprecision(2)
<< t_dSpeed << " MBit/s" << std::endl;
// response
for (int i = 0; i < CYCLE_TIME; ++i) {
if (i == CYCLE_TIME - 1) {
t_strResponse[t_strResponse.size() - 1] = 1;
}
int t_iSendBytes = send(t_iNewSocket, t_strResponse.c_str(), \
t_strResponse.size(), 0);
if (t_iSendBytes != t_strResponse.size()) {
std::cout << "send error" << std::endl;
return -1;
}
else {
std::cout << "Send message successfully. "
<< std::endl << "CNT = " << i << std::endl
<< "size: " << t_iSendBytes << " Bytes." << std::endl;
}
}
// close
close(t_iNewSocket);
std::cout << "t_lRecvTotalBytes = "
<< t_lRecvTotalBytes << " Bytes" << std::endl;
std::cout << "t_lRecvTotalTime = "
<< t_lRecvTotalTime << " microseconds." << std::endl;
std::cout << "network speed: "
<< std::setprecision(2)
<< t_dSpeed << " MBit/s" << std::endl;
}
return 0;
}
客户端.cpp
#include <iostream>
#include <arpa/inet.h>
#include <string>
#include <chrono>
#include <unistd.h>
#include <stdlib.h>
#include <iomanip>
#include <signal.h>
#define CYCLE_TIME (100)
#define PORT (7549)
#define LOCAL_MODE (0)
#define REMOTE_MODE (1)
int g_iSockFd;
void sigQuitHandler(int signal) {
std::cout << "received SIGQUIT, doing graceful shutting down ..."
<< std::endl;
close(g_iSockFd);
exit(1);
}
void sigIntrHandler(int signal) {
std::cout << "received SIGINT, doing graceful shutting down ..."
<< std::endl;
close(g_iSockFd);
exit(1);
}
int main(int argc, char** argv) {
int t_iWorkMode = LOCAL_MODE;
if (argc == 2) {
int t_iTempWorkMode = atoi(argv[1]);
switch (t_iTempWorkMode) {
case LOCAL_MODE:
t_iWorkMode = LOCAL_MODE;
break;
case REMOTE_MODE:
t_iWorkMode = REMOTE_MODE;
break;
default:
std::cout << "mode should be 1 or 2. LOCAL_MODE is used."
<< std::endl;
t_iWorkMode = LOCAL_MODE;
}
}
// register signal handler function
struct sigaction t_struQuitSigAction;
struct sigaction t_struIntrSigAction;
t_struQuitSigAction.sa_handler = sigQuitHandler;
t_struQuitSigAction.sa_flags = 0;
sigemptyset(&t_struQuitSigAction.sa_mask);
sigaction(SIGQUIT, &t_struQuitSigAction, 0);
t_struIntrSigAction.sa_handler = sigIntrHandler;
t_struIntrSigAction.sa_flags = 0;
sigemptyset(&t_struIntrSigAction.sa_mask);
sigaction(SIGINT, &t_struIntrSigAction, 0);
struct in_addr t_in_addr;
memset(&t_in_addr, 0, sizeof(t_in_addr));
if (inet_aton("127.0.0.1", &t_in_addr) == 0) {
std::cout << "inet_aton fail." << std::endl;
return -1;
}
std::string t_strMsg;
char t_cRecvBuffer[102400];
// init socket parameter
struct sockaddr_in t_struSockAddr;
memset(&t_struSockAddr, 0, sizeof(t_struSockAddr));
// IPv4
t_struSockAddr.sin_family = AF_INET;
// remote IP address
if (t_iWorkMode == REMOTE_MODE) {
t_struSockAddr.sin_addr.s_addr = inet_addr("remote server IP address");
}
else {
t_struSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
}
// remote port
t_struSockAddr.sin_port = htons(PORT);
// create socket
int g_iSockFd = socket(AF_INET, SOCK_STREAM, 0);
if (g_iSockFd == -1) {
std::cout << "Error accurs when creating socket."
<< std::endl << "System exits abnormally." << std::endl;
return -1;
}
if (connect(g_iSockFd, (struct sockaddr *)&t_struSockAddr, \
sizeof(struct sockaddr)) < 0) {
std::cout << std::endl
<< "Error accurs when connecting to server."
<< std::endl << "System exits abnormally." << std::endl;
return -1;
}
std::cout << "Connected to remote server." << std::endl;
int t_iSendBytes = 0;
// send request
t_strMsg = std::string(10240, 0);
for (int i = 0; i < CYCLE_TIME; ++i) {
// 1 means the end of message
if (i == CYCLE_TIME - 1) {
t_strMsg[t_strMsg.size() - 1] = 1;
}
t_iSendBytes = send(g_iSockFd, t_strMsg.c_str(), \
t_strMsg.size(), 0);
if (t_iSendBytes != t_strMsg.size()) {
std::cout << "Request message is not "
<< "sent completely. System exits abnormally. "
<< "t_iSendBytes = " << t_iSendBytes << " Bytes. " << std::endl;
return -1;
}
else {
std::cout << "CNT = " << i << std::endl
<< "Msg ends with "
<< int(t_strMsg[t_strMsg.size() - 1]) << std::endl
<< "Send request message. Bytes: " << t_iSendBytes
<< std::endl;
}
}
std::cout << "Send finished." << std::endl;
std::cout << "Begin to receive ..." << std::endl;
long t_lRecvTotalBytes = 0;
long t_lRecvTotalTime = 0;
double t_dSpeed = 0.0;
std::chrono::high_resolution_clock::time_point t_StartTime;
std::chrono::high_resolution_clock::time_point t_EndTime;
t_StartTime = std::chrono::high_resolution_clock::now();
memset(t_cRecvBuffer, 0, sizeof(t_cRecvBuffer));
while (1) {
int t_iRecvBytes = recv(g_iSockFd, t_cRecvBuffer, sizeof(t_cRecvBuffer), 0);
if (t_iRecvBytes == -1) {
std::cout << "Error accurs when receiving. "
<< std::endl << "System exits abnormally.";
return -1;
}
else if (t_iRecvBytes > 0) {
t_lRecvTotalBytes += t_iRecvBytes;
std::string t_strRecv(t_cRecvBuffer, t_iRecvBytes);
// std::cout << t_strRecv;
std::cout << "Receiving " << t_iRecvBytes << "/"
<< t_lRecvTotalBytes << " bytes." << std::endl;
// 1 means the end of message.
if (t_cRecvBuffer[t_iRecvBytes - 1] == 1) {
std::cout << "recv finished." << std::endl;
break;
}
}
}
t_EndTime = std::chrono::high_resolution_clock::now();
t_lRecvTotalTime =
std::chrono::duration_cast<std::chrono::microseconds>(
t_EndTime - t_StartTime
).count();
t_dSpeed = double(t_lRecvTotalBytes) / t_lRecvTotalTime
* 1000000 * 8 / 1024 / 1024;
std::cout << "t_lRecvTotalBytes = "
<< t_lRecvTotalBytes << " Bytes" << std::endl;
std::cout << "t_lRecvTotalTime = "
<< t_lRecvTotalTime << " microseconds." << std::endl;
std::cout << "network speed: "
<< std::setprecision(2)
<< t_dSpeed << " MBit/s" << std::endl;
// close
close(g_iSockFd);
return 0;
}
目标
目标是我的客户端和服务器程序之间的网络速度可以达到大约 80 Mbits/sec。但我想不出解决问题的方法。而且utorrent和iperf测试的网速让我一头雾水。
如果有人可以帮助我,我将不胜感激。谢谢!
解决方案
推荐阅读
- node.js - 节点 8.11.1 上的阿波罗联盟订阅
- c - 如何停止一次又一次地重复相同的数据?
- python - 如何使用 Python 在 MySQL 中创建唯一的 ID 字段
- django - django rest框架嵌套关系视图
- python - Tkinter:仅在按下鼠标按钮时获取鼠标坐标
- c# - ZXing Aztec 条码 UTF-8
- python-3.x - statsmodels AutoReg.predict 的开始/结束参数的含义
- django - django 中的“Tech”和“Mobile”实例之间不支持“<”
- python-3.x - 在Databricks(DBFS)中递归列出目录和子目录的文件
- c# - 如何使用 JsonSerializerSettings 在反序列化期间忽略属性