Root-me TCP返校挑战C++实现求助:结果发送失败排查
解决root-me.org「TCP-Back to school」挑战的发送问题
问题背景
- 挑战任务:通过IPv6 TCP连接到
2001:bc8:35b0:c166::151:52002,解析服务器发送的数学题计算num1的平方根并乘以num2,将结果四舍五入到两位小数,在2秒内以二进制int/float类型发送给服务器(注意:不是字符串形式) - 遇到的问题:
- 发送字符串格式的结果时,服务器返回
[!] Please only send int/float ! - 直接发送float精度不足,发送double无响应,多数情况下服务器无反馈
- 发送字符串格式的结果时,服务器返回
当前代码的核心问题
你的代码错误地将计算结果转换为ASCII字符串发送,而服务器要求的是二进制格式的数值数据(即直接发送float或int类型的内存字节)。此外:
- 使用
ceil实现向上取整,不符合挑战要求的四舍五入 - 发送字符串时存在拼接错误(如测试输出中出现
8807.008807这类异常内容)
修复方案
关键改动点
- 用
round()函数实现正确的四舍五入到两位小数 - 将计算结果转换为32位float类型,直接发送其二进制内存数据
- 确保发送的字节长度为
sizeof(float),而非字符串长度
修改后的完整代码
// TCP - Retour au collège #include <sys/socket.h> #include <iostream> #include <cmath> #include <cstring> #include <netinet/in.h> #include <unistd.h> #include <iomanip> #include <arpa/inet.h> #include <string> int main() { // Connect to root-me.org with tcp (this script is client side) int client = socket(AF_INET6, SOCK_STREAM, 0); if (client == -1) { std::cerr << "Socket creation failed" << std::endl; return 1; } // set socket address type to network type struct sockaddr_in6 serv; std::memset(&serv, 0, sizeof(serv)); serv.sin6_family = AF_INET6; serv.sin6_port = htons(52002); if (inet_pton(AF_INET6, "2001:bc8:35b0:c166::151", &serv.sin6_addr) != 1) { std::cerr << "Invalid IPv6 address" << std::endl; close(client); return 1; } // Connect to server if (connect(client, (struct sockaddr*)&serv, sizeof(serv)) == -1) { std::cerr << "Connection failed" << std::endl; close(client); return 1; } std::cout << "Connected to port 52002" << std::endl; //Receive data and processing char buffer[1024] = { 0 }; ssize_t recv_len = recv(client, buffer, sizeof(buffer)-1, 0); if (recv_len <= 0) { std::cerr << "Receive failed or server closed connection" << std::endl; close(client); return 1; } buffer[recv_len] = '\0'; // Ensure null termination std::cout << "Message from serv:\n" << buffer << std::endl; std::string str = buffer; size_t sqrt_pos = str.find("square root of "); size_t multiply_pos = str.find(" and multiply by "); size_t equal_pos = str.find(" = "); // Parse numbers with error checking if (sqrt_pos == std::string::npos || multiply_pos == std::string::npos || equal_pos == std::string::npos) { std::cerr << "Failed to parse server message" << std::endl; close(client); return 1; } std::string number1 = str.substr(sqrt_pos + strlen("square root of "), multiply_pos - (sqrt_pos + strlen("square root of "))); int number1_ = std::stoi(number1); std::string number2 = str.substr(multiply_pos + strlen(" and multiply by "), equal_pos - (multiply_pos + strlen(" and multiply by "))); int number2_ = std::stoi(number2); double res = std::sqrt(number1_) * number2_; std::cout << "Raw result: " << std::setprecision(15) << res << std::endl; // Correct rounding to two decimal places res = std::round(res * 100.0) / 100.0; std::cout << "Rounded result: " << std::fixed << std::setprecision(2) << res << std::endl; // Convert to float and send as binary float res_float = static_cast<float>(res); ssize_t send_len = send(client, &res_float, sizeof(res_float), 0); if (send_len != sizeof(res_float)) { std::cerr << "Failed to send full data" << std::endl; close(client); return 1; } std::cout << "Sent binary float (size: " << sizeof(res_float) << " bytes)" << std::endl; // Receive server response char buffer1[1024] = { 0 }; ssize_t recv_len1 = recv(client, buffer1, sizeof(buffer1)-1, 0); if (recv_len1 > 0) { buffer1[recv_len1] = '\0'; std::cout << "\nServer response:\n" << buffer1 << std::endl; } else { std::cerr << "No response from server or connection closed" << std::endl; } //closing&ending close(client); return 0; }
改动说明
- 增加了错误检查:socket创建、连接、接收、解析步骤的错误处理,避免程序崩溃
- 替换
ceil为round:实现标准四舍五入(ceil是向上取整,会导致结果偏大) - 发送逻辑修改:直接发送
float类型的内存数据,长度为sizeof(float)(通常4字节),而非字符串 - 确保字符串解析的正确性:调整了子串截取的计算方式,避免越界
测试验证
运行修改后的代码后,服务器应该会返回正确的响应(包含挑战的flag)。如果仍然无响应,检查:
- 是否在2秒内完成计算和发送(当前代码的计算逻辑足够快,不会超时)
- 网络环境是否允许访问IPv6地址
- 是否正确处理了服务器的完整消息(增加的错误检查会提示解析失败的情况)
内容的提问来源于stack exchange,提问作者Get
相关产品推荐
相关产品推荐

