在本文中,我将描述在Arduino Mega Server项目的开发过程中遇到的情况。最重要的是,有一个这样的Arduino以太网库,用于支持W5100芯片上的Ethernet Shield网卡。这是与Arduino开发环境捆绑多年的标准板和标准库。该库是使用有线网络上的信息交换的所有项目的基础。因此,事实证明该库根本不合适。原则上,不可能在此基础上建立正常的网络交互。您只能“沉迷”单个请求和响应。我们不能谈论基于此库构建任何服务器。为什么?因为该库具有内置的“错误”,可将非唯一请求暂停三到十秒或更长时间。该错误是内置的,并且库的作者对此有所了解,如他在源中的注释所证明的(稍后会对此进行更多介绍)。在这里,您需要了解官方开发环境附带的库是某个标准,如果该项目不适合您,那么您将在任何地方(而不是在标准库中)查找缺陷,因为数十年来(即使不是数百万)已经使用了数十年。人。他们都错了吗?为什么实际上在Arduino上没有服务器
该项目的开发按预期进行,最后是优化代码并提高了服务器的速度,在这里我面临以下情况:接收到来自浏览器的传入请求,并将其“暂停”平均三到十秒钟,最多二十到二十秒钟。交流时间更长,秒数更多。这是一个截图,显示了服务器响应中的异常延迟在各种请求之间“走动”。
当我开始理解时,事实证明,没有什么可以阻止服务器响应,但是,请求“挂起”了超过9秒钟,而在另一次迭代中,相同的请求已经挂起了大约3秒钟。这样的观察使我陷入了沉思,并挖出了整个服务器代码(同时又费劲了),但没有发现缺陷,所有逻辑都导致了“圣洁”的Arduino以太网库。但是,认为标准库应归咎于煽动性的想法被认为是不够的。实际上,不仅用户在使用该库,而且还有大量的专业开发人员。他们难道都看不到这么明显的东西吗?展望未来,我要说的是,当事实证明它是标准库时,很明显为什么实际上没有(普通)Arduino服务器。因为基于标准库(大多数开发人员都在使用),所以基本上不可能构建服务器。十秒或更长的响应延迟会使服务器脱离服务器本身的类别,使其成为一个简单的(讨厌的)玩具。中级退出。这不是Arduino不适合构建服务器的情况,并且网络库结束了非常有趣的一类设备。问题剖析
现在,让我们从歌词转到对问题及其实际解决方案的技术描述。对于那些不是最新的人,标准库位置(在Windows上):arduino \ libraries \ Ethernet我们首先要看的是EthernetServer.cpp文件中的函数。EthernetClient EthernetServer::available() {
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
return client;
}
}
}
return EthernetClient(MAX_SOCK_NUM);
}
当我克服了心理障碍(在逻辑压力下)并开始在以太网库中查找缺陷时,我就开始使用此功能。我注意到作者的一则奇怪的评论,但对此并不重视。在铲除了整个库之后,我又重新使用了几天,但是在网络技术方面取得了长足的进步之后,我又回到了此功能,因为逻辑表明问题出在这里,并且仔细地看了一下评论。
朋友,一切都以明文形式写成。在免费的翻译中,这听起来像是这样:“它有效,但并非总是如此。”等一下,“不总是”是什么意思?我们没有周日乐透俱乐部。当它不起作用时,那又如何呢?但是,当“行不通”并且问题开始时会延迟十秒钟。作者肯定知道这一点,他的创作的自尊心即证明了这一点-三x。没意见。该库是许多克隆的基础,并且请注意,这三个X从一个项目漫游到另一个项目。如果您是开发人员,那么在不测试网络交换的情况下,您只会一次注意到此问题。也没有评论。对于那些不太熟悉代码的人,我将用简单的词来解释问题的实质。循环遍历套接字,并在找到合适的套接字后立即返回客户端,而忽略其余部分。他们挂了十秒钟,直到“卡片顺利躺下”。解决问题
了解了问题的原因之后,我们当然不会止步并尝试解决问题。首先,让我们重写该函数,以便接收或不接收套接字都不取决于情况的意愿,并且总是在有套接字的情况下发生。这将解决两个问题:- 请求不会挂起
- “顺序”请求将变为“并行”,这将大大加快工作速度
因此,新功能的代码如下:EthernetClient EthernetServer::available_(int sock) {
accept_(sock);
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
return client;
}
}
return EthernetClient(MAX_SOCK_NUM);
}
我们删除循环,显式指定套接字,并且不会丢失任何内容-如果它是免费的,那么我们保证会收到一个客户端(如果它适合我们)。我们对accept函数的代码进行相同的“链接”,删除循环,并显式指定套接字。void EthernetServer::accept_(int sock) {
int listening = 0;
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port) {
if (client.status() == SnSR::LISTEN) {
listening = 1;
}
else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) {
client.stop();
}
}
if (!listening) {
begin_(sock);
}
}
并且不要忘记修复EthernetServer.h文件class EthernetServer :
public Server {
private:
uint16_t _port;
void accept_(int sock);
public:
EthernetServer(uint16_t);
EthernetClient available_(int sock);
virtual void begin();
virtual void begin_(int sock);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
using Print::write;
};
就这样。我们对标准库进行了更改,服务器行为也发生了巨大变化。如果早期的一切工作都非常缓慢,超出了可用性的任何想法,那么现在页面加载速度已经大大提高,对于正常使用来说已经可以接受了。
注意将不同文件的延迟减少3-5倍,并且下载的性质完全不同,这在实际使用中非常明显。修改后的EthernetServer.cpp的完整代码/*
Mod for Arduino Mega Server project
fix bug delay answer server
*/
#include «w5100.h»
#include «socket.h»
extern «C» {
#include «string.h»
}
#include «Ethernet.h»
#include «EthernetClient.h»
#include «EthernetServer.h»
EthernetServer::EthernetServer(uint16_t port) {
_port = port;
}
void EthernetServer::begin() {
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (client.status() == SnSR::CLOSED) {
socket(sock, SnMR::TCP, _port, 0);
listen(sock);
EthernetClass::_server_port[sock] = _port;
break;
}
}
}
void EthernetServer::begin_(int sock) {
EthernetClient client(sock);
if (client.status() == SnSR::CLOSED) {
socket(sock, SnMR::TCP, _port, 0);
listen(sock);
EthernetClass::_server_port[sock] = _port;
}
}
/*
void EthernetServer::accept() {
int listening = 0;
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port) {
if (client.status() == SnSR::LISTEN) {
listening = 1;
}
else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) {
client.stop();
}
}
}
if (!listening) {
begin();
}
}
*/
void EthernetServer::accept_(int sock) {
int listening = 0;
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port) {
if (client.status() == SnSR::LISTEN) {
listening = 1;
}
else if (client.status() == SnSR::CLOSE_WAIT && !client.available()) {
client.stop();
}
}
if (!listening) {
//begin();
begin_(sock); // added
}
}
/*
EthernetClient EthernetServer::available() {
accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
// XXX: don't always pick the lowest numbered socket.
return client;
}
}
}
return EthernetClient(MAX_SOCK_NUM);
}
*/
EthernetClient EthernetServer::available_(int sock) {
accept_(sock);
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
(client.status() == SnSR::ESTABLISHED ||
client.status() == SnSR::CLOSE_WAIT)) {
if (client.available()) {
return client;
}
}
return EthernetClient(MAX_SOCK_NUM);
}
size_t EthernetServer::write(uint8_t b) {
return write(&b, 1);
}
size_t EthernetServer::write(const uint8_t *buffer, size_t size) {
size_t n = 0;
//accept();
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
accept_(sock); // added
EthernetClient client(sock);
if (EthernetClass::_server_port[sock] == _port &&
client.status() == SnSR::ESTABLISHED) {
n += client.write(buffer, size);
}
}
return n;
}
修改后的EthernetServer.h的完整代码/*
Mod for Arduino Mega Server project
fix bug delay answer server
*/
#ifndef ethernetserver_h
#define ethernetserver_h
#include «Server.h»
class EthernetClient;
class EthernetServer:
public Server {
private:
uint16_t _port;
//void accept();
void accept_(int sock);
public:
EthernetServer(uint16_t);
//EthernetClient available();
EthernetClient available_(int sock);
virtual void begin();
virtual void begin_(int sock);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
using Print::write;
};
#endif
仍然存在的问题
在这种形式下,演示想法的服务器进入了可以在日常生活中使用的事物类别,但是仍然存在一些问题。正如您在屏幕快照中看到的那样,仍然没有一个基本的延迟,而是令人不快的三秒钟延迟,这不是应该的。该库的编写方式使得很多地方代码无法正常工作,如果您是合格的开发人员,那么帮助确定三秒钟延迟的原因将非常有价值。既适用于Arduino Mega Server项目,也适用于所有Arduino用户。最后一刻
由于我们更改了标准库的代码,因此我们需要以稍微不同的方式调用其函数。在这里,我给出了真正有效的代码,该代码在上面的屏幕快照中提供了AMS。 for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient sclient = server.available_(sock);
serverWorks2(sclient);
}
在这里,对套接字进行排序的任务已经转移到了客户端草图的级别,最重要的是,以上所有含义是什么,没有“冻结”请求。服务器本身的功能:void serverWorks2(EthernetClient sclient) {
...
}
您可以通过从Arduino Mega Server项目的官方网站下载分发套件来熟悉完整的服务器代码。您可以在论坛上提问。解决最后一个三秒延迟的问题还有待解决,我们将在Arduino上拥有一个真正的,快速工作的服务器。顺便说一下,不久将发布新版本的AMS,其中包含所有已解决的最紧迫问题之一的所有更正和改进-不支持MajorDoMo服务器的脱机工作。
这很大程度上是由于我刚刚告诉您的标准Arduino以太网库的更正。加法。一个Youtube频道已打开,这是Arduino Mega Server 的促销视频,演示了如何在真实系统上工作。