我正在尝试解决 root-me.org 的挑战,这是一个非常简单的挑战,但我仍然陷入困境,无法弄清楚问题是什么。 问题:
声明
要使用 TCP 协议启动此测试,您需要连接到网络套接字上的程序。
计算数字 1 的平方根并乘以数字 2。
然后将结果四舍五入到小数点后两位。
从程序向您发送计算结果后,您有 2 秒的时间发送正确答案。
答案必须以int形式发送
根据我迄今为止发现的情况,问题出在我尝试将响应发送到服务器的部分。
指向我试图解决的挑战的链接:https://www.root-me.org/en/Challenges/Programming/TCP-Back-to-school
我尝试了四种不同的方式来发送答案:使用浮点数、字符串和整数,在某一时刻我只收到了服务器的响应,它说我发送了错误的类型,“答案有是 int 或 float”,我什至不记得我是如何得到这个回应的。
此外,请注意,我只能发送具有正确值和精度的字符串,因为 c++ 中的浮点数缺乏太多精度。例如: 发送双精度数会导致发送具有正确精度的数字 (38559.390000),但小数点会被“0”填充 发送浮点数会导致错误的结果:(8807.008807) 发送字符串有效,但显然服务器不支持它。
我的代码:
// 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);
// set socket address type to network type
struct sockaddr_in6 serv;
std::memset(&serv, 0, sizeof(serv));
// set type to ipv6
serv.sin6_family = AF_INET6;
// Set port
serv.sin6_port = htons(52002);
//convert to bin and add actual adress
inet_pton(AF_INET6, "2001:bc8:35b0:c166::151", &serv.sin6_addr);
// Bind the client to the parameters
connect(client, (struct sockaddr*)&serv, sizeof(serv));
std::cout << "port 52002" << std::endl;
//Receive data and processing
char buffer[1024] = { 0 };
recv(client, buffer, sizeof(buffer), 0);
std::cout << "Message from serv: " << buffer << std::endl;
std::string str = buffer;
size_t startPos1 = str.find("square root of ") + strlen("square root of ");
size_t startPos2 = str.find(" and multiply by ") + strlen(" and multiply by ");
size_t endPos1 = str.find(" and multiply by ") - 1;
size_t endPos2 = str.find(" =") - 1;
std::string number1 = str.substr(startPos1, endPos1 - startPos1 + 1);
int number1_ = std::stoi(number1);
std::string number2 = str.substr(startPos2, endPos2 - startPos2 + 1);
int number2_ = std::stoi(number2);
double res = (std::sqrt((number1_))) * number2_; //static_cast<double>
std::cout << std::setprecision (15) << res << std::endl;
//seend - HERE I TRIED MANY DIFFERENT THINGS, SENDING A STRING, A DOUBLE AND A FLOAT
//float res_ = static_cast<float>(res);
//std::cout << res_ << std::endl;
//std::string res_=std::to_string(res);
//res_.erase(res_.size() - 4);
//std::cout << "string:" << res_ << std::endl;
//char buffer2[sizeof(res_)];
//std::memcpy(buffer2, &res_, sizeof(res_));
res = std::ceil(res * 100.0) / 100.0;
std::string a = std::to_string(res);
size_t pos = (a.find(".")+3);
std::string b = a.substr(0, pos);
std::cout << b << std::endl;
const char* message = b.c_str();
send(client, message, strlen(b.c_str()), 0);
std::cout << "sent :" << b.c_str() << ";" << message << std::endl;
//Response?
char buffer1[1024] = { 0 };
recv(client, buffer1, sizeof(buffer1), 0);
std::cout << "Message from serv: " << buffer1 << std::endl;
//closing&ending
close(client);
return 0;
}
输出: 我唯一一次得到服务人员的回复:
port 52002
Message from serv:
====================
GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 1 and multiply by 8807 =
8807.00
sent :8807.008807
Message from serv: [!] Please only send int/float !
输出 1 -
port 52002
Message from serv:
==================== GO BACK TO COLLEGE====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 207 and multiply by 2092 =
30098.6386403106
sent :30098.64
输出 2 -
port 52002
Message from serv:
====================
GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 120 and multiply by 3958 =
43357.7176521089
43357.72
sent :43357.72;43357.72
输出 3 -
port 52002
Message from serv:
====================
GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 87 and multiply by 4134 =
38559.3850054692
string:38559.39
sent :38559.39;38559.390000
输出 4 -
port 52002
Message from serv:
====================
GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 56 and multiply by 9112 =
68187.9642165683
68187.97sent :68187.97;R�����@R�����@�x�
recv(client, buffer1, sizeof(buffer1), 0);
忽略返回值。 TCP 是一种流协议,不尊重您通过调用发送暗示的任何消息传递。多个发送将被打包到一个数据包中,单个
send
的内容可能会被分割成多个数据包,并且在数据包传输时可能会发生各种丑陋的情况。您得到的唯一保证是数据将按发送顺序传送,否则您会收到错误消息。为了确保您获得所需的所有数据,您需要检查 recv
的返回代码并循环,直到获得所需的所有数据。由于您似乎正在发送和接收以 null 结尾的字符串,因此您需要循环直到获得 null 终止符。
char ch = ' '; // init to anything but a null
while (ch) // loop until ch is null
{
int rval = recv(client, &ch, 1, 0);
switch (rval)
{
case 0: // disconnected without finishing message
// log and exit
return EXIT_FAILURE;
case 1: // got a character.
// Put it in buffer
break;
default: // error code
// decode error message into human readable string
// log error
return EXIT_FAILURE;
}
}
这很慢而且丑陋,但是很简单。一旦你开始工作,就可以考虑读入缓冲区并扫描缓冲区中的空终止符。如果缓冲区中存在空值,则传递该消息进行处理,并保留缓冲区中空值之后的任何数据以供将来处理。如果不为空,则再次
recv
并继续,直到找到终止符,收集到的数据不再可能是消息,或者缓冲区已满。
此外,
send(client, message, strlen(b.c_str()), 0);
省略空终止符(并且不检查返回代码以确保发送整个消息。切勿忽略返回代码!)。接收器无法知道何时停止读取,并且很可能继续读取垃圾区或将其写入字符数组,而显示代码不知道何时停止读取。
解决方案:发送空终止符
send(client, message, strlen(b.c_str()) + 1, 0);
并且不要忘记检查
send
的返回代码。 send
不承诺发送完整消息。它只承诺发送它能发送的内容。返回代码将告诉您发送了多少数据或发生了错误。请注意,无法发送您想要发送的所有数据不被视为错误。
好吧,我实际上是通过用 python 编写解决方案来解决这个问题的,从而解锁了网站的“解决方案”类别,在那里我可以找到我曾经激发过我灵感的 C 代码版本。
这基本上就是我所做的(没有剧透):
首先,我删除了以下两个代码示例之间的部分,并从头开始重新发送数据,因为这只是一个巨大的混乱
double res = (std::sqrt((number1_))) * number2_; //static_cast<double>
std::cout << std::setprecision (15) << res << std::endl;
================================删除================== ========================
//Response?
char buffer1[1024] = { 0 };
在数据处理结束时,我输出一个双精度值
double res = (std::sqrt((number1_))) * number2_; //static_cast<double>
在我使用“osstring”:
std::ostringstream oss;
定义“oss”之后,我将双“res”分配给“oss”(类似于oss << [...] << std::setprecision(2) << res
),这强制字符串“oss”有两位小数,并且它们是准确的,因为我使用的是 double,它比 c++ 中的 float 对象更准确。
然后我发回我的数据(将其更改为字符串后):
send(client, strn.c_str(), strlen(strn), 0);
最后,我监听服务器响应,如上面的代码所示。