【发布时间】:2018-10-11 02:04:33
【问题描述】:
我的 C++ 客户端套接字代码只收集 Arduino 套接字服务器提供的输出的第一行(我想我可以这么称呼它)。你能告诉我我的 C++ 代码哪里出了问题以及如何解决它吗?如果问题太冗长,请跳到底部的 C++ 代码。
硬件设置:带有以太网卡(屏蔽)的 Arduino Mega 和带有 Ubuntu 16.04 的英特尔 NUC。这两个设备使用电缆和非管理型交换机连接。
Arduino Side:我从 Arduino 以太网库中的一个 Web 服务器示例开始,并修改了代码,直到我能够收集所有 I/O 的状态,处理 I/O数据,并将结果提供给 Web 客户端。下图显示了我的 Arduino 输出的 HTML 快照。
处理套接字服务器端的 Arduino 代码如下(一切正常,但认为包含显示一切):
#include <SPI.h>
#include <Ethernet.h>
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};
IPAddress ip(192, 168, 0, 21);
EthernetServer server(80);
void setup()
{
Ethernet.begin(mac, ip);
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware)
{
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware.");
while (true)
{
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF)
{
Serial.println("Ethernet cable is not connected.");
}
server.begin();
}
void loop()
{
//true when there is an incoming connection
if (client)
{
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected())
{
if (client.available())
{
//the next two lines print the client HTTP GET request to serial.
char c = client.read();
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank)
{
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println("Refresh: 1");
client.println();
client.println("<!DOCTYPE HTML>");
client.println("<html>");
//whole bunch of client.println("...."); to dish out a web page.
client.println("</html>");
break;
}
if (c == '\n')
{
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r')
{
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
//delay(1);
// close the connection:
client.stop();
Serial.println("client disconnected");
}
}
带有 Ubuntu 16.04 和 C++11 的 Intel NUC:我一直在关注教程 (https://www.binarytides.com/socket-programming-c-linux-tutorial/) 来了解套接字客户端,并从 https://www.learncpp.com 学习 C++。到目前为止,我能够向 Google 服务器发送请求并使用我的套接字收集 HTML 页面,并将 HTML 打印到终端:
这是我的 C++ 代码:
#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<string.h> // for strlen
int main()
{
int my_socket = socket(AF_INET , SOCK_STREAM , 0);
//make sure the socket we got is OK
if (my_socket == -1)
{
std::cout << "problem creating a socket." << std::endl;
}
struct sockaddr_in connectionToServer;
connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
connectionToServer.sin_port = htons(80); //port is set via a method
if (connect(my_socket , (struct sockaddr *)&connectionToServer , sizeof(connectionToServer)) < 0)
{
std::cout << "connect error" << std::endl;
return 1;
}
std::cout << "Connected" << std::endl;
//send a request to get a page
char *message = "GET / HTTP/1.1\r\n\r\n";
if( send(my_socket , message , strlen(message) , 0) < 0)
{
std::cout << "Send failed" << std::endl;
return 1;
}
std::cout << "Data Sent\n" << std::endl;
char buffer [20000] = {};
if( recv(my_socket, buffer , sizeof(buffer) , 0) < 0)
{
std::cout << "recv failed" << std::endl;
}
for(int i=0; i<20000 ; i++)
{
std::cout<< buffer[i];
}
return 0;
}
问题:当我在我的 C++ 程序中将 IP 地址从 Google 更改为我的 Arduino 时,cpp 客户端套接字程序只收集 Arduino 套接字服务器输出的第一行。我知道这是第一行,因为我通过添加“..但没关系”修改了 Arduino 服务器的第一行,并且更改显示在 c++ 程序的标准输出窗口中。我需要 C++ 程序来收集整个输出,而不仅仅是第一行。对于我的一生,我无法弄清楚如何。
您能帮我解决以下问题吗:
- 如何收集整个 Arduino 消息(而不仅仅是第一行)?我需要对我的 C++ 程序进行哪些修改?系统需要能够将数据从一个设备传递到另一个设备。
- 此设置的全部目标是将 2 个浮点数和 6 个整数从 Arduino 传递到我的 C++ 程序。很快我将取消整个 HTML 的东西。在传递数据时,您会推荐我使用哪种协议?我正在考虑用字母填充每个值。例如:“Aint1A Bint2B Cint3C Dfloat1D ...”等等。您能否推荐一些帖子/教程/网页,这些帖子/教程/网页建议了打包和处理通过套接字到达 C++ 程序的数据的最佳方式?
我提前为“fix-my-codez”问题道歉,但我读过的所有问题对我来说都太高级了,因为它们涉及缓冲区溢出、安全性、字节序问题、错误处理、格式错误的消息等on,这远远超出了我的需要(能力可能更准确)。非常感谢您的时间和帮助。
【问题讨论】:
-
recv()不保证一次性返回所有数据。您需要循环调用它。 -
您的 C++ 代码实际上并未实现 HTTP 协议。特别是,没有任何代码可以接收 HTTP 回复。因此,我们的代码无法接收 HTTP 回复,而是从 TCP 流中接收到任意数据块也就不足为奇了。
-
@Mike 您的 HTTP 请求格式错误(缺少必需的
Host标头)。但更重要的是,您只调用了 1 次recv(),因此您会收到最大 1..20000 字节之间的任何位置,其中 MAY 或 MAY NOT(可能不)包含一口气完整回复。您需要循环调用recv(),直到到达响应的实际结尾。这对于 HTTP 来说并不简单,因为 HTTP 是一种具有消息传递结构的协议,而您根本没有遵循该结构,甚至没有接近。您必须改用 logic like this pseudo code。