TCP发送15字符至GCP VM时75-200μs RTT是否正常?
问题描述
我正在开发一个基于C++的RTT(ping)实时监控项目:客户端运行在本地机器,服务端部署在Google Cloud VM的Docker容器中。目前遇到的问题是计算出的RTT异常偏低(75-200微秒),和客户端、服务端同机运行时的结果几乎一致。按预期,通过TCP向外部服务器发送数据的RTT应该更长,不确定是对TCP/网络的理解有误,还是代码存在问题。已确认VM端有日志输出,排除了误连本地服务的情况。
客户端代码(client.cpp)
#define _WIN32_WINNT 0x0600 #include <iostream> #include <winsock2.h> #include <chrono> #include <ctime> #include <ws2tcpip.h> #include <matplot/matplot.h> #define PORT 8080 #define MAX_BUFFER_SIZE 1024 #define TRUE 1 #define FALSE 0 int main() { WSADATA wsaData; SOCKET clientSocket; sockaddr_in serverAddr; const char* helloMessage = "Hello from client"; char buffer[MAX_BUFFER_SIZE]; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed.\n"; return 1; } if ((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { std::cerr << "Could not create socket.\n"; return 1; } serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(PORT); if (InetPton(AF_INET, "MY GCLOUD IP", &serverAddr.sin_addr) <= 0) { std::cerr << "Invalid address/Address not supported.\n"; return 1; } if (connect(clientSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) { std::cerr << "Connection failed.\n"; return 1; } double iteration = 0; std::vector<double> iterations; std::vector<double> pings; using namespace matplot; line_handle fig = plot(iterations, pings); fig.get()->display_name("Real-time ping monitoring"); while(TRUE) { auto start = std::chrono::high_resolution_clock::now(); send(clientSocket, helloMessage, strlen(helloMessage), 0); int recvSize = recv(clientSocket, buffer, MAX_BUFFER_SIZE, 0); auto end = std::chrono::high_resolution_clock::now(); int64_t ping = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); buffer[recvSize] = '\0'; std::cout << "Server: " << buffer << std::endl; std::cout << "Time elapsed: " << ping << "us\n"; pings.push_back(ping); iterations.push_back(++iteration); try { fig.get()->x_data(iterations); fig.get()->y_data(pings); fig.get()->touch(); } catch (const std::runtime_error& e) { std::cerr << "Caught a runtime_error exception: " << e.what() << '\n'; } Sleep(1000); } closesocket(clientSocket); WSACleanup(); return 0; }
服务端代码(server.cpp)
#include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <cstring> #include <csignal> #include <iostream> #define PORT 8080 #define MAX_BUFFER_SIZE 1024 int serverSocket; void cleanUpOnCrash(int sig) { close(serverSocket); std::cout << "Server closed\n"; exit(0); } int main() { signal(SIGINT, cleanUpOnCrash); sockaddr_in serverAddr, clientAddr; char buffer[MAX_BUFFER_SIZE]; socklen_t clientAddrSize = sizeof(clientAddr); // Create a socket if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { std::cerr << "Could not create socket.\n"; return 1; } // Bind the socket to an IP address and port serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(PORT); if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) { std::cerr << "Bind failed.\n"; return 1; } // Listen for incoming connections listen(serverSocket, 3); // Accept a client connection while(1){ std::cout << "Waiting for incoming connections...\n"; int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrSize); if (clientSocket == -1) { std::cerr << "Accept failed.\n"; return 1; } std::cout << "Connection accepted.\n"; // Send a welcome message to the client const char* welcomeMessage = "Welcome to the server!\n"; send(clientSocket, welcomeMessage, strlen(welcomeMessage), 0); // Echo back any messages received from the client int recvSize; while ((recvSize = recv(clientSocket, buffer, MAX_BUFFER_SIZE, 0)) > 0) { buffer[recvSize] = '\0'; // Null-terminate the received string before printing std::cout << "Client: " << buffer << std::endl; send(clientSocket, buffer, recvSize, 0); } // Clean up close(clientSocket); } return 0; }
问题分析与修复方案
问题根源
当前计时逻辑没有准确测量真实的网络RTT,核心原因:
- TCP缓冲区与Nagle算法:TCP是面向连接的协议,
connect建立连接后,后续send的数据会先存入操作系统发送缓冲区,recv也可能直接从本地接收缓冲区读取数据,而非等待公网链路往返。加上Nagle算法会合并小数据包,进一步弱化了真实网络延迟的体现。 - 计时点偏差:
send调用成功仅代表数据拷贝到缓冲区,不代表已发送到网络;recv成功也可能是读取本地缓存,导致计时结果偏向本地操作耗时,而非真实RTT。
修复方案
1. 禁用Nagle算法(可选但推荐)
在客户端connect成功后添加代码,避免小数据包合并:
int flag = 1; setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag));
2. 优化计时逻辑
确保测量真实的网络往返:
- 客户端:发送数据后,需等待服务端的真实响应(而非本地缓存);可在
send后检查发送字节数,确保数据完整写入缓冲区。 - 对比验证:先用系统
ping命令测试GCLOUD IP的真实RTT,若结果为几十毫秒,说明程序计时确实存在偏差。
3. 改用UDP实现(更适合ping场景)
UDP是无连接协议,每次发送都需要完整的网络往返,能更准确测量RTT,避免TCP连接复用和缓冲区优化的干扰。
内容的提问来源于stack exchange,提问作者TheMorgan8697
相关产品推荐
相关产品推荐

