回到学校-TCP;扎根我;应该可以,但不行 - c++ C++

问题描述 投票:0回答:2

我正在尝试解决 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�

c++ sockets tcp network-programming challenge-response
2个回答
0
投票
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
不承诺发送完整消息。它只承诺发送它能发送的内容。返回代码将告诉您发送了多少数据或发生了错误。请注意,无法发送您想要发送的所有数据不被视为错误。


-1
投票

好吧,我实际上是通过用 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);

最后,我监听服务器响应,如上面的代码所示。

© www.soinside.com 2019 - 2024. All rights reserved.