Проблема с telnet-сервером.
- Войдите на сайт для отправки комментариев
Сб, 26/03/2016 - 09:48
Добрый день.
Есть задача отдавать температуру с датчика подключенного к ардиуине через telnet-соединение.
Запущено всё это на Arduino Mega + Ethernet Shield W5100.
Всё впринципе работает как требудется за исключением одного момента: необходимо, чтобы после открытия клиентом соедиенения на telnet-порт ему сразу же выдавалась температура и соединение закрывалось, без ожидания ввода какого-либо запроса (можно это назвать welcome message). Сейчас же у меня при открытии соединения сервер всегда ждет ввода от клиента прежде чем что-то вывести в ответ.
Как же отключить ожидание ввода от клиента после соединения и сразу ему выдавать сообщение?
Скетч:
client = server.available();
if (client) {
client.println("Welcome message"); // Данное сообщение не отображается пока не нажмешь Enter
delay(1);
// close the connection:
client.stop();
}
Поправка: не обязательно Enter ждет, а просто ввода любого символа на клавиатуре и после этого сразу же выдает сообщение.
А что по-вашему делает строка 1 (с последующей проверкой условия в строке 2)?
Не поверите, но ждёт пока с сервера что-нибудь не придёт! :)
В таком случае может подскажите как сделать так, как требуется - отвечать клиенту сразу без ожидания запроса? Не могу найти примеры подобного, везде только с обработкой входящих команд.
Похоже в библиотеке Ethernet вообще нет такой возможности.
так вам уже ответили. строка 1,2 и 7 ...
Я уже разобрался как работают эти строки - выбор клиента, с ожиданием ввода, так что не надо мне на них по десять раз указывать. Спрашивал я как добиться требуемого функционала - вывод ответа без запроса ввода.
Ни хрена Вы не разобрались. Вернее, ни хрена не поняли. Всё, что Вам нужно, Вам уже сказали.
Попробую объяснить по-другому.
Вы вообще знаете что такое клиент-серверная архитектура? Чем отличается клиент от сервера?
Ваша программа - сервер, а сервер никогда никуда и ничего не шлёт без запроса - его серверное дело обслуживать клиентов. Запросил клиент - шлёт, не запросил - не шлёт.
так что Вам надо идейно менять эту программу, а не секретные функции в библиотеке искать.
Добавлю ещё. TCP протоколы это не передача в эфир потока, это установления свзи по некоторому протоколу и беспечение обмена, в т.ч. обработка нарушения последовательности пакетов, пропадания пакетов. Т.ч. это не сом-порт в иной среде где слушают вход и отправляют на выход произвольно. В ethernet это уровень MAC. Если необходимо получать от сервера инфу не спрашивая этот сервер, ройте в сторону UDP - когда сервер может вещать без запроса. Но телнет, разумеется, на UDP не летает.
Спасибо, конечно, за лекцию по клиент-серверной архитектуре, но можно это делать и не грубя.
Как она работает я в курсе, и открытие сетевого соединения (сокета), совсем не означает, что клиент должен в него что-то отправить - он вполне может его слушать на предмет получения данных от сервера (того же приветствия). В билиотеке же ардуины единственный способ обработать подключение клиента к серверу это проверка отправил ли клиент какие-либо данные серверу.
В результате поисков нашел, что сами разработчики библиотеки в курсе данного ограничения использования их библиотеки, и в январе они обсуждали вариант ввести в класс EthernetServer публичную функцию accept, для обработки именно события открытия сокета без ожидания ввода.
Если у Вас есть вариант решения этой задачи на текущий момент, я его готов выслушать. Только пускай на этот раз это будет не простой отсылкой к голой теории по стеку tcp, а кокретным примером с использованием библиотеки Ethernet. Я думаю таким специалистам это не составит большого труда, написать 5-6 строк кода;)
Если бы все клиенты постоянно висели подключенными к серверу и просто периодически по запросу получали некие данные с сервера, либо подключались к нему и отправляли запрос, то проблем бы не было как и данной темы, но проблема в том, что есть уже готовые, давно функционирующие клиенты, и действуют они по вышеописанному принципу - открывают tcp-соединение, и сразу же считывают ответ сервера, далее закрывают содинение.
В библиотеке же единственный способ отловить момент подключения клиента и отправить данные именно ему это функция EthernetServer.available(), которая, ждет ввода от клиента. EthernetServer.write(). Тоже не подходит, поскольку, как я уже писал - клиенты не висят постоянно подключенными.
Что мешает подправить библиотеку? Она и так кривоватая, так что беды не будет особо ;)
Ну, даже не знаю, вариант решения проблемы Вам тут озвучивали и я и другие коллеги - просто поменять идеологию взаимодействия, а для этого поменять библиотеку или написать свою.
Чего Вы ещё ждёте? Что кто-то поменяет/напишет за Вас? Так Вам в "Ищу исполнителя", такие запросы там размещаются.
Немного своего добавлю Вы говорите о 5-м, сеансовом уровне. А телнет работает на 6-м уровне представления. Т.е., кроме самого трансопрта у телнета есть свой протокол, в котором рукопожатие для начала обмена используется. Собственно, такова вообще идеология и так модно в сетевых протоколах клиент-сервер: рукопожатие, запрос-ответ. Хотя, часто бывает и запрос-ответ, ответ, ответ. Но поведение телнет-клиента, когда ему сразу при подключении дают данные, я не изучал, да ивообще очень давно его изучал.
Это уже не совсем телнет будет, это Вы телнетовым языком т.с. и телнетовым клиентом лишь будете пользоваться, который, по сути, обычный TTY терминал с небольшим набором команд, начинающихся с 255h, за что его и оставили в жизни. :) В Вашем случае, похоже, телнет не нужен, Вам необходим просто пакет данных от сервера при установлении соединения. И тут я соглашусь с предыдушими товарищами - придётся самому поправить код библиотеки дуни - они все в исходниках же лежат (только не забутьде изменения сохранить за пределаит их местоположения, чтобы при обновлении версии изменения не улетели). Или двигаться в сторону взрослых средств. :)
Ну, даже не знаю, вариант решения проблемы Вам тут озвучивали и я и другие коллеги - просто поменять идеологию взаимодействия, а для этого поменять библиотеку или написать свою.
Чего Вы ещё ждёте? Что кто-то поменяет/напишет за Вас? Так Вам в "Ищу исполнителя", такие запросы там размещаются.
Ну, была надежда, что кто-то уже сталкивался с таким, и есть какаие-то наработки. Раз нет, буду пробовать колупать библиотеку:)
Это уже не совсем телнет будет, это Вы телнетовым языком т.с. и телнетовым клиентом лишь будете пользоваться, который, по сути, обычный TTY терминал с небольшим набором команд, начинающихся с 255h, за что его и оставили в жизни. :) В Вашем случае, похоже, телнет не нужен, Вам необходим просто пакет данных от сервера при установлении соединения. И тут я соглашусь с предыдушими товарищами - придётся самому поправить код библиотеки дуни - они все в исходниках же лежат (только не забутьде изменения сохранить за пределаит их местоположения, чтобы при обновлении версии изменения не улетели). Или двигаться в сторону взрослых средств. :)
Вы правы, похоже понятие "telnet-сервер" я сюда приплел зря, и немного сбил с толку отвечающих. Мне, действительно, всего лишь нужно получить некие данные от сервера при подключении к нему и сразу же закрыть соединение.
А до взрослых стредств пока рановато, надо в "песочнице" наиграться:)
Дам небольшой отчет - проблема решена добавлением в библиотеку функции EhernetServer::connected(), в которой обрабатывается факт подключения без ожидания ввода.
EthernetClient EthernetServer::connected() { accept(); for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { EthernetClient client(sock); if (EthernetClass::_server_port[sock] == _port) { uint8_t s = client.status(); if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { return client; } } } return EthernetClient(MAX_SOCK_NUM); }Дам небольшой отчет - проблема решена добавлением в библиотеку функции EhernetServer::connected(), в которой обрабатывается факт подключения без ожидания ввода.
EthernetClient EthernetServer::connected() { accept(); for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { EthernetClient client(sock); if (EthernetClass::_server_port[sock] == _port) { uint8_t s = client.status(); if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { return client; } } } return EthernetClient(MAX_SOCK_NUM); }Во! Дело! Но от меня совет на основе своих шишек - не вносите изменения в библиотеки дуни, ибо она эти изменения запросто покоцает обновлением. Лучше сделать свой новый объект наследником и в нём добавить медот.
ибо она эти изменения запросто покоцает обновлением.
Она, что у Вас сама обновляется? Ну, Вы эстет.
Дам небольшой отчет - проблема решена добавлением в библиотеку функции EhernetServer::connected(), в которой обрабатывается факт подключения без ожидания ввода.
EthernetClient EthernetServer::connected() { accept(); for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { EthernetClient client(sock); if (EthernetClass::_server_port[sock] == _port) { uint8_t s = client.status(); if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { return client; } } } return EthernetClient(MAX_SOCK_NUM); }Это хорошо, что сделали, но: как я уже упоминал - кривая она, стандартная либа Ethernet. Предлагаю помедитировать над комментарием и понять, почему будут случаться зависоны остальных клиентов, пока с одним идёт интенсивная работа. Там даже комментарий чудный есть, обратите на него внимание:
EthernetClient EthernetServer::available() { accept(); for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { EthernetClient client(sock); if (EthernetClass::_server_port[sock] == _port) { uint8_t s = client.status(); if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { if (client.available()) { // XXX: don't always pick the lowest numbered socket. return client; } } } } return EthernetClient(MAX_SOCK_NUM); }Ну не пишутся так стандартные либы в поставке, блин! Всего же делов: добавить переменную, в которой хранить номер клиента, которого надо проверить следующим, при переходе этого номера в MAX_SOCK_NUM - сбрасывать номерок в ноль. При каждом вызове available() - инкрементировать, профит, блин.
Ничего - придёт моя W5100 - поправлю я это пиздобразие наконец-то: уж сколько лет валяется этот комментарий, и никому до него дела нет. Серьёзный подход, млять.
З.З.Ы. Либы в стандартной поставке, за редким исключением - для серьёзных проектов не годны чуть более, чем полностью. Имхо, конечно.
Ну не пишутся так стандартные либы в поставке, блин! Всего же делов: добавить переменную, в которой хранить номер клиента, которого надо проверить следующим, при переходе этого номера в MAX_SOCK_NUM - сбрасывать номерок в ноль. При каждом вызове available() - инкрементировать, профит, блин.
Ничего - придёт моя W5100 - поправлю я это пиздобразие наконец-то: уж сколько лет валяется этот комментарий, и никому до него дела нет. Серьёзный подход, млять.
З.З.Ы. Либы в стандартной поставке, за редким исключением - для серьёзных проектов не годны чуть более, чем полностью. Имхо, конечно.
Да песочница же оно. :) Попробовать, покрутить часть устройства. Или начать знакомство с мегой/стмкой. Например, есть библа для работы с экранчиком - вот мне он нафиг не нужен чтобы самому разбираться, но удобен для отладки. Прикрутил и фиг с ним насколько он фиговый, в дальнейшем не нужен.
Кто будет писать в дуне серьёзныей проект? Оно же, всего лишь, блокнот с парой фишек. Да ещё на Яве написаный - ничго в жизни не видел написанного на яве некривого. На Яве вообще принято не думать, ляпать на фремворке - компоновать из готовых. Причём, чертовски неудобный "блокнот". В старой версии дуни в комплекте даже был "Progammer notepad". :)
Во! Дело! Но от меня совет на основе своих шишек - не вносите изменения в библиотеки дуни, ибо она эти изменения запросто покоцает обновлением. Лучше сделать свой новый объект наследником и в нём добавить медот.
Я пока что сделал проще - вынес библиотеку в пользовательскую папку, и при компиляции приоритет отдается ей, а не встроенной, так что обновление ничего не похерит.
Это хорошо, что сделали, но: как я уже упоминал - кривая она, стандартная либа Ethernet. Предлагаю помедитировать над комментарием и понять, почему будут случаться зависоны остальных клиентов, пока с одним идёт интенсивная работа. Там даже комментарий чудный есть, обратите на него внимание:
EthernetClient EthernetServer::available() { accept(); for (int sock = 0; sock < MAX_SOCK_NUM; sock++) { EthernetClient client(sock); if (EthernetClass::_server_port[sock] == _port) { uint8_t s = client.status(); if (s == SnSR::ESTABLISHED || s == SnSR::CLOSE_WAIT) { if (client.available()) { // XXX: don't always pick the lowest numbered socket. return client; } } } } return EthernetClient(MAX_SOCK_NUM); }Ну не пишутся так стандартные либы в поставке, блин! Всего же делов: добавить переменную, в которой хранить номер клиента, которого надо проверить следующим, при переходе этого номера в MAX_SOCK_NUM - сбрасывать номерок в ноль. При каждом вызове available() - инкрементировать, профит, блин.
Ничего - придёт моя W5100 - поправлю я это пиздобразие наконец-то: уж сколько лет валяется этот комментарий, и никому до него дела нет. Серьёзный подход, млять.
З.З.Ы. Либы в стандартной поставке, за редким исключением - для серьёзных проектов не годны чуть более, чем полностью. Имхо, конечно.
Да-да, я уже натыкался при поиске на статью, где про эту "чудную" реализацию и комментарий было написано, но пока руки ещё не дошли до более глубокой правки библиотеки - W5100 только пару дней назад пришла)))
Не забудьте поделится своими наработками, когда получите и "поиграетесь" с платой;)