इस लेख में, मैं उस स्थिति का वर्णन करूंगा जो मैंने Arduino Mega Server प्रोजेक्ट के विकास के दौरान सामना किया था । लब्बोलुआब यह है कि ऐसी एक Arduino ईथरनेट लाइब्रेरी है, जिसे W5100 चिप पर ईथरनेट शील्ड नेटवर्क कार्ड का समर्थन करने के लिए लिखा गया है। यह एक मानक बोर्ड और मानक पुस्तकालय है जिसे कई वर्षों से Arduino विकास पर्यावरण के साथ जोड़ा गया है।और यह लाइब्रेरी उन सभी परियोजनाओं का आधार है जो वायर्ड नेटवर्क पर सूचनाओं के आदान-प्रदान का उपयोग करते हैं। तो, यह पता चला कि यह पुस्तकालय बस अनुपयुक्त है। उस पर, सिद्धांत रूप में, एक सामान्य नेटवर्क इंटरैक्शन का निर्माण करना असंभव है। आप केवल एकल अनुरोधों और प्रतिक्रियाओं में "लिप्त" हो सकते हैं। हम इस लाइब्रेरी के आधार पर किसी भी सर्वर के निर्माण के बारे में बात नहीं कर सकते। क्यों?क्योंकि इस लाइब्रेरी में एक अंतर्निहित "बग" है जो तीन से दस सेकंड या उससे अधिक की अवधि के लिए गैर-अद्वितीय अनुरोधों को निलंबित करता है। बग को बनाया गया है और पुस्तकालय के लेखक को इस बारे में पता था, जैसा कि स्रोत में उनके नोट्स द्वारा स्पष्ट किया गया था (लेकिन बाद में और अधिक)।यहां आपको यह समझने की आवश्यकता है कि आधिकारिक विकास के वातावरण के साथ आने वाली लाइब्रेरी एक निश्चित मानक है और यदि परियोजना आपके लिए काम नहीं करती है, तो आप कहीं भी दोष की तलाश करेंगे, लेकिन मानक पुस्तकालय में नहीं, क्योंकि इसका उपयोग कई वर्षों से सैकड़ों, यदि लाखों नहीं है लोगों का। क्या वे सब गलत नहीं हो सकते?क्यों प्रकृति में Arduino पर कोई सर्वर नहीं हैं
परियोजना का विकास उस तरह से होना चाहिए और अंत में, यह कोड को अनुकूलित करने और सर्वर की गति बढ़ाने के लिए आया था, और यहां मुझे निम्न स्थिति का सामना करना पड़ा: ब्राउज़र से आने वाले अनुरोध प्राप्त हुए और तीन से दस सेकंड की अवधि के लिए "निलंबित", औसतन, बीस तक और अधिक तीव्र मुद्रा के साथ अधिक सेकंड। यहां एक स्क्रीनशॉट है जो दिखाता है कि विभिन्न अनुरोधों के दौरान सर्वर प्रतिक्रियाओं "चलना" में असंगत देरी है।
जब मैंने समझना शुरू किया, तो यह पता चला कि कुछ भी सर्वर को जवाब देने से नहीं रोक रहा था, लेकिन, फिर भी, अनुरोध "लटका हुआ" नौ सेकंड से अधिक समय तक, और एक अन्य पुनरावृत्ति पर पहले से ही लगभग तीन सेकंड के लिए लटका हुआ था।इस तरह की टिप्पणियों ने मुझे एक गहरी विचारशीलता में डुबो दिया और मैंने पूरे सर्वर कोड (एक ही समय में खुद को फैलाया) को खोदा, लेकिन कोई दोष नहीं पाया और सभी तर्क "पवित्र के पवित्र" Arduino ईथरनेट लाइब्रेरी का नेतृत्व किया। लेकिन राजद्रोही ने सोचा कि मानक पुस्तकालय को दोष देने के लिए अपर्याप्त माना गया था। दरअसल, न केवल उपयोगकर्ता पुस्तकालय के साथ काम करते हैं, बल्कि बड़ी संख्या में पेशेवर डेवलपर्स भी हैं। क्या वे सभी इतनी स्पष्ट चीजें नहीं देख सकते हैं?आगे देखते हुए, मैं कहता हूं कि जब यह पता चला कि मामला मानक पुस्तकालय में था, तो यह स्पष्ट हो गया कि प्रकृति में अरुडिनो पर कोई (सामान्य) सर्वर क्यों नहीं हैं। क्योंकि मानक पुस्तकालय (जो अधिकांश डेवलपर्स के साथ काम करते हैं) के आधार पर सर्वर बनाना मूल रूप से असंभव है। दस सेकंड या उससे अधिक की प्रतिक्रिया में देरी सर्वर को स्वयं सर्वर की श्रेणी से बाहर ले जाती है और इसे एक सरल (आकर्षक) खिलौना बनाती है।इंटरमीडिएट वापसी। यह सर्वरों के निर्माण के लिए उपयुक्त नहीं है, और नेटवर्क लाइब्रेरी उपकरणों के एक बहुत ही दिलचस्प वर्ग का अंत करता है।समस्या का एनाटॉमी
अब गीत से समस्या के तकनीकी वर्णन और इसके व्यावहारिक समाधान पर आगे बढ़ते हैं। जो लोग अप टू डेट नहीं हैं, उनके लिए मानक लाइब्रेरी लोकेशन (विंडोज पर):arduino \ lbooks \ 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);
}
जब मैंने मनोवैज्ञानिक बाधा (तर्क के दबाव में) को काबू किया और ईथरनेट लाइब्रेरी में दोष की तलाश शुरू की, तो मैंने इस फ़ंक्शन के साथ शुरुआत की। मैंने लेखक द्वारा एक अजीब टिप्पणी पर ध्यान दिया, लेकिन इसके लिए कोई महत्व नहीं दिया। पूरी लाइब्रेरी को हिला देने के बाद, मैंने फिर से, लेकिन कुछ दिनों के बाद और नेटवर्क प्रौद्योगिकियों में बहुत उन्नति की, इस समारोह में वापस आ गया क्योंकि तर्क ने सुझाव दिया कि समस्या वहाँ थी और टिप्पणी पर अधिक बारीकी से देखा।
दोस्तों, सब कुछ स्पष्ट पाठ में लिखा गया है। एक मुफ्त अनुवाद में, यह कुछ ऐसा लगता है: "यह काम करता है, लेकिन हमेशा नहीं।" एक मिनट रुको, हमेशा "नहीं" का क्या मतलब है? हमारे पास संडे लोट्टो क्लब नहीं है। और जब यह काम नहीं करता है, तो क्या? लेकिन जब "काम नहीं करता है" और समस्याएं दस सेकंड की देरी से शुरू होती हैं।और लेखक निश्चित रूप से इस बारे में जानता था, जैसा कि उसकी रचना के आत्मसम्मान से स्पष्ट है - तीन एक्स। कोई टिप्पणी नहीं। यह पुस्तकालय कई क्लोनों का आधार है और, ध्यान, ये तीनों एक्स एक परियोजना से दूसरी परियोजना में घूमते हैं। यदि आप एक डेवलपर हैं, तो आप केवल नेटवर्क एक्सचेंज का परीक्षण किए बिना इस समस्या को नोटिस नहीं कर सकते। इसके अलावा कोई टिप्पणी नहीं।उन लोगों के लिए जो कोड में खराब रूप से पारंगत हैं, मैं सरल शब्दों में समस्या का सार बताऊंगा। लूप सॉकेट्स पर निर्भर करता है और जैसे ही वह उपयुक्त पाता है, क्लाइंट को वापस कर देता है, और बस बाकी को अनदेखा कर देता है। और वे दस सेकंड के लिए लटकाते हैं, जब तक "कार्ड अनुकूल रूप से झूठ नहीं बोलते।"समस्या का समाधान
समस्या के कारण को समझने के बाद, हम निश्चित रूप से वहाँ नहीं रुकेंगे और इसे हल करने का प्रयास करेंगे। सबसे पहले, फ़ंक्शन को फिर से लिखते हैं ताकि सॉकेट प्राप्त करना या प्राप्त न करना मामले की इच्छा पर निर्भर नहीं करता है और हमेशा ऐसा होता है यदि कोई हो। यह दो समस्याओं को हल करेगा:- अनुरोध लटका नहीं होगा
- "अनुक्रमिक" अनुरोध "समानांतर" में बदल जाएंगे, जिससे काम में काफी तेजी आएगी
तो, नए फ़ंक्शन के लिए कोड: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);
}
हम लूप को हटाते हैं, स्पष्ट रूप से सॉकेट निर्दिष्ट करते हैं और कुछ भी नहीं खोते हैं - यदि यह मुफ़्त है, तो हमें एक ग्राहक प्राप्त करने की गारंटी है (यदि यह हमें सूट करता है)।हम स्वीकार समारोह के कोड के साथ एक ही "चेनिंग" करते हैं, लूप को हटाते हैं, और सॉकेट को स्पष्ट रूप से निर्दिष्ट करते हैं।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 मेगा सर्वर परियोजना के लिए और सभी Arduino उपयोगकर्ताओं के लिए दोनों।अंतिम क्षण
चूंकि हमने मानक पुस्तकालय के कोड को बदल दिया है, इसलिए हमें इसके कार्यों को थोड़ा अलग तरीके से कॉल करने की आवश्यकता है। यहां मैं कोड देता हूं जो वास्तव में काम करता है और जो ऊपर स्क्रीनशॉट में एएमएस प्रदान करता है। for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient sclient = server.available_(sock);
serverWorks2(sclient);
}
यहां, सॉकेट को सॉर्ट करने का कार्य क्लाइंट स्केच के स्तर पर स्थानांतरित किया गया है, और, सबसे महत्वपूर्ण बात, और उपरोक्त का क्या अर्थ है, अनुरोधों का कोई "ठंड" नहीं है। और स्वयं सर्वर का कार्य:void serverWorks2(EthernetClient sclient) {
...
}
आप Arduino Mega Server प्रोजेक्ट की आधिकारिक साइट से वितरण किट डाउनलोड करके अपने आप को पूर्ण सर्वर कोड से परिचित कर सकते हैं । और आप मंच पर अपने प्रश्न पूछ सकते हैं । यह तीन-सेकंड की देरी की अंतिम समस्या को हल करने के लिए बनी हुई है और हमारे पास Arduino पर एक वास्तविक, तेजी से काम करने वाला सर्वर होगा। वैसे, जल्द ही एएमएस का एक नया संस्करण उन सभी सुधारों और सुधारों के साथ जारी किया जाएगा, जिनमें से एक को दबाने वाली समस्याओं का समाधान किया गया है - मेजरडोमो सर्वर के समर्थन के बिना ऑफ़लाइन काम।
और यह काफी हद तक संभव हो गया मानक अरडूइनो ईथरनेट लाइब्रेरी के सुधार के कारण जो मैंने आपको अभी बताया था।जोड़ । एक Youtube चैनल खुला है और यहाँ Arduino Mega Server का प्रोमो वीडियो है , जो दर्शाता है कि वास्तविक प्रणाली के साथ कैसे काम किया जाए।