C++ Socket Programming: Network Low Speed Analysis
/ code0
I have bought a VPS as a remote server and deployed shadowsocks as a proxy on this remote server.
Case 1:
When I use uTorrent to download files through shadowsocks, the speed could reach 10 MB/sec (which is 80 Mbits/sec). The diagram is:
--------------------------- -------------------------- --------- | Local computer | | Remote server | | | | ------------ | 80 Mbits/sec | ----------- | | | | uTorrent<-->| shadowsocks||<---------------->| |shadowsocks|<--------------->|Internet | | | client || | | server | | | | | ------------ | | ----------- | | | --------------------------- -------------------------- ---------
Case 2:
Now, I wrote a client/server program by C++. But the network speed is extremely low, and here is my test results:
Client: download speed
t_lRecvTotalBytes = 1024000 Bytes
t_lRecvTotalTime = 2942410 microseconds.
network speed: 2.7 MBit/s
Server: upload speed
t_lRecvTotalBytes = 1024000 Bytes
t_lRecvTotalTime = 1741625 microseconds.
network speed: 4.5 MBit/s
The diagram is:
--------------------------- -------------------------- | Local computer | | Remote server | | --------------- |2.7~4.5 Mbits/sec | -------------- | | | My C++ client |<------------------------->| My C++ server| | | --------------- | | -------------- | | | | | --------------------------- --------------------------
Case 3:
iperf3
is used to test the bandwith between local computer
and Remote server
. And the result is:
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.00 sec 7.25 MBytes 6.08 Mbits/sec
[ 5] 10.00-20.00 sec 11.6 MBytes 9.73 Mbits/sec
[ 5] 20.00-30.00 sec 12.2 MBytes 10.3 Mbits/sec
[ 5] 30.00-40.00 sec 11.4 MBytes 9.53 Mbits/sec
[ 5] 40.00-50.00 sec 12.0 MBytes 10.0 Mbits/sec
[ 5] 50.00-60.00 sec 8.28 MBytes 6.94 Mbits/sec
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 62.9 MBytes 8.79 Mbits/sec 222 sender
[ 5] 0.00-60.00 sec 62.7 MBytes 8.77 Mbits/sec receiver
The diagram is:
--------------------------- -------------------------- | Local computer | | Remote server | | --------------- | 8.8 Mbits/sec | -------------- | | | iperf3 client |<------------------------->| iperf3 server| | | --------------- | | -------------- | | | | | --------------------------- --------------------------
MY CLIENT/SERVER CODE
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; }
client.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; }
GOAL
The goal is that the network speed between my client and server program could reach about 80 Mbits/sec. But I cannot figure out a way to solve the problem. And the network speed tested by utorrent and iperf make me confused.
I will appreciate it if anyone can help me. Thank you!