Ry_Mik Posted May 6 Posted May 6 Добрый день! Подскажите пожалуйста, как посмотреть что происходит с сокетом? Какие конкретно ошибки и где они возникают? Работаю с проприетарным трекером положения, который в свою очередь передаёт свои данные через стороннее проприетарное ПО. Используется TCP/UDP соединение и передача данных по следующей схеме: Клиент -> TCP (просто соединение на укзанном порту)-> ПО ответ ПО -> TCP с указанием порта UDP, который надо начать слушать, чтобы получать данные Клиент -> UDP по полученному выше порту -> получение данных. К сожалению обойти этот "костыль" я никак не могу (хотя очень хочется конечно))). Так вот. Реализация у меня есть и она работает. Вопрос возник в другом - а что делать, если вдруг стороннее ПО отвалится по той или иной причине? Хочется сделать так, чтобы в случае чего соединение восстанавливалось само. Базовый запуск получения данных выглядит следующим образом: void TrackerHandler::InitTracker() { InitHandlerTimer(); InitDataBuffer(); if (!InitTCPSocket()) return; // Выполняет первое TCP соединение со сторонним ПО while (IsTCPinit() && _timer.endMilliseconds() < _INIT_DELAY_MS) { if (debug) { /* Временная простая заглушка, просто ждёт TrackerHandler::_INIT_DELAY_MS мс перед началом запуска UDP */ Unigine::Log::message("wait %i ms before UDP initialization\n", _INIT_DELAY_MS); } } // RecieveUDPPort() - получает и дешифрует сообщение по TCP протоколу // Передает полученный порт в метод инициализации UDP сокета if (!InitUDPSocket(RecieveUDPPort())) return; ResetHandelerTimer(); _is_init = true; } Важно. "Костыль" работает на постоянном TCP соединении, если отключиться то передача данных по UDP остановится. Таким образом оба сокета остаются активными. Инициализация сокетов реализована следующим образом: bool TrackerHandler::InitTCPSocket() { if (IsTCPinit()) return true; if (_tcp == nullptr) { _tcp = Unigine::Socket::create(Unigine::Socket::SOCKET_TYPE::SOCKET_TYPE_STREAM, _host.data(), _port); } if (debug) { Unigine::Log::message("Try TCP connection to %s:%i\n", _host.data(), _port); } if (!(_tcp_connect = _tcp->connect())) return _tcp_connect; if (!_tcp->nonblock()) { throw std::runtime_error("TrackerHandler::InitTCPSocket()::ERROR::TCP Socket nonblock setting is fault\n"); } return (bool)_tcp; } bool TrackerHandler::InitUDPSocket(int port) { if (IsUDPinit()) return true; if (debug) { Unigine::Log::message("UDP port is %i\n", port); } if (port == -1) return false; if (_udp == nullptr) { _udp = Unigine::Socket::create(Unigine::Socket::SOCKET_TYPE::SOCKET_TYPE_DGRAM); } if (!(_upd_open = _udp->open(port))) return false; if (!_udp->recv((int)GetBufferSize())) { throw std::runtime_error("TrackerHandler::InitUDPSocket()::ERROR::Recieve buffer size setting error\n"); } if (!_udp->bind()) { throw std::runtime_error("TrackerHandler::InitUDPSocket()::ERROR::UDP Socket bind is fault\n"); } if (!_udp->nonblock()) { throw std::runtime_error("TrackerHandler::InitUDPSocket()::ERROR::UDP Socket nonblock setting is fault\n"); } return (bool)_udp; } Если "костыль" изначально запущен или его таки запускают через какое-то время, то благодаря update() происходит инициализация сокетов, данные начинают поступать и всё работает. Соответственно если вдруг он отваливается, то поток останавливается. Стал копать какие есть у сокетов флаги состояния. bool TrackerHandler::IsTCPinit() const { // Для UDP почти то же самое сделал if (debug) { if (!_tcp) { Unigine::Log::message("TCP Socket ptr is not init\n"); } else { Unigine::Log::message("TCP Socket debug information\n"); Unigine::Log::message("TCP Socket is valid - %s\n", _tcp->isValid() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is opened - %s\n", _tcp->isOpened() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is connect - %s\n", _tcp_connect ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is avalible - %s\n", _tcp->isAvailable() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is ready to read - %s\n", _tcp->isReadyToRead() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is ready to write - %s\n", _tcp->isReadyToWrite() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is deleted - %s\n", _tcp->isDeleted() ? "TRUE" : "FALSE"); Unigine::Log::message("TCP Socket is error - %s\n\n", _tcp->isError() ? "TRUE" : "FALSE"); } } return _tcp != nullptr && _tcp_connect; } И вот что привлекло моё внимание: ... выдержка из консоли ... Script loading "core/unigine.usc" 54ms World loading "cpp-unigine-components.world" (Time: 32.3ms, Memory: 915.5KB) TCP Socket ptr is not init <--- соекта еще нет, создаём Try TCP connection to 127.0.0.1:40124 <--- выполняем подключение (оно успешно, так как "костыль" работает) TCP Socket debug information <--- статусы выводятся из-за ожидания в цикле while (запуск пошаговый, время прошло) TCP Socket is valid - TRUE TCP Socket is opened - TRUE TCP Socket is connect - TRUE TCP Socket is avalible - FALSE TCP Socket is ready to read - TRUE TCP Socket is ready to write - TRUE TCP Socket is deleted - FALSE TCP Socket is error - FALSE <--- нет ошибок!!! Try read TCP socket <--- читаем сообщение от стороннего ПО Try resolve UDP port <--- получаем порт UDP Socket ptr is not init <--- создаём сокет UDP port is 58447 <--- вывод в консоль полученного порта от TCP сокета TCP Socket debug information TCP Socket is valid - TRUE TCP Socket is opened - TRUE TCP Socket is connect - TRUE TCP Socket is avalible - FALSE TCP Socket is ready to read - FALSE TCP Socket is ready to write - TRUE TCP Socket is deleted - FALSE TCP Socket is error - TRUE <--- откуда ошибка?! что пошло не так? что случилось? UDP Socket debug information <--- UDP порт работает никаких нареканий UDP Socket is valid - TRUE UDP Socket is opened - TRUE UDP Socket is avalible - FALSE UDP Socket is ready to read - TRUE UDP Socket is ready to write - TRUE UDP Socket is deleted - FALSE UDP Socket is error - FALSE <--- UDP нет ошибок ... далее выводятся статусы уже по TCP и UDP и у обоих сокетов есть ошибки, хотя передача данных идёт корректно и сбоев в работе нет Для получения данных использую метод read(), в который передаю буфер void* предварительно очищенный от предыдущих данных. Вопрос 1 - откуда, почему и как возникают ошибки? Ошибки на обоих сокетах. Вопрос 2 - если не с помощью флага ошибки, то как отследить потерю соедиения? Спасибо!
cash-metall Posted May 6 Posted May 6 1. по поводу методов для сокета isValid/isDeleted - методы указателя, не сокета. показывают жив ли экземпляр объекта или уже удален. isOpened - проверяет открыт ли сокет. после закрытия вернем false. isAvaliable - для сокета всегда возвращает 0 isReadyToRead/isReadyToWrite выполняет соответствующий select на сокет isError - вот тут интереснее. я не уверен что этот метод работает корректно, потому что вместе с выставлением ошибки в сокете должно писаться сообщение об ошибке в консоль. везде кроме пары мест: size_t res = write / size; if (res != nmemb) is_error = 1; когда методы read/write считали или записали меньше или больше элементов чем планировалось (*!) тут надо смотреть конкретно логику записи/чтения. к сожалению после этого этот флаг не снимается и до конца жизни будет возвращать true даже не смотря на то, что все работает. поэтому не стоит обращать на него внимания, выглядит что в этом месте просто баг, не влияющий на работу. 2. по поводу потери соединения - к сожалению протоколы tcp/udp не подразумевают уведомления о непредвиденном разрыве. Если внезапно кто-то непредвиденно пропал - сокет будет висеть активным еще какое то время, так называемые полуоткрытые сокеты, время жизни которых определяется ОС вот тут можно найти подробнее https://habr.com/ru/articles/173415/ https://ru.wikipedia.org/wiki/Полуоткрытое_TCP/IP-соединение Единственный способ обнаруживать такой разрыв — это попытаться что-то отправить в сокет. Возможно, слышали слово heartbeat — это периодическая отправка небольшого сообщения серверу (клиенту) и ожидание ответа. Делается именно для того, чтобы обнаруживать полуоткрытые соединения. * для анализа сетевого трафика я рекомендую использовать програмку wireshark - чтобы понять что именно летает по сокетам и почему возникает ошибка чтения/записи. 1
Ry_Mik Posted May 6 Author Posted May 6 Спасибо за подсказки! Посмотрю что можно придумать. У меня ситуёвина отягощается тем, что я не знаю как именно работает "костыль". Всё что мне сказали: "при подключении по TCP тебе присылается уведомление с UDP портом, на который высылаются данные". Ну и формат данных сказали. На этом всё) Потому я даже не знаю, есть ли какие-то еще команды для проверки работоспособности? нету? Ну и плюс моя неопытность конечно же.. может быть я вообще задаю вопросы, которые "и так всем понятны и известны")
Recommended Posts