Files
pip/libs/main/io_utils/pipacketextractor.cpp
peri4 1c7fc39b6c version 4.0.0_alpha
in almost all methods removed timeouts in milliseconds, replaced to PISystemTime
PITimer rewrite, remove internal impl, now only thread implementation, API similar to PIThread
PITimer API no longer pass void*
PIPeer, PIConnection improved stability on reinit and exit
PISystemTime new methods
pisd now exit without hanging
2024-07-30 14:18:02 +03:00

330 lines
10 KiB
C++

/*
PIP - Platform Independent Primitives
Packets extractor
Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pipacketextractor.h"
#include "piliterals.h"
/** \class PIPacketExtractor
* \brief Packets extractor
* \details
* \section PIPacketExtractor_main Synopsis
* This class implements packet recognition by various algorithms and custom
* validating from data stream. Stream is formed from child %PIIODevice
* passed from contructor or with function \a setDevice().
*
* \section PIPacketExtractor_work Principle of work
* %PIPacketExtractor works with child %PIIODevice. \a read and \a write
* functions directly call child device functions. You should start threaded
* read of \b extractor (not child device) to proper work. Extractor read data
* from child device, try to detect packet from readed data and raise
* \a packetReceived() event on success.
*
* \section PIPacketExtractor_algorithms Algorithms
* There are 6 algorithms: \n
* * PIPacketExtractor::None \n
* Packet is successfully received on every read without any validation. \n \n
* * PIPacketExtractor::Header \n
* Wait for at least \a header() bytes + \a payloadSize(), then validate
* header with virtual function \a validateHeader() and if it fail, shifts
* for next 1 byte. If header is successfully validated check payload with
* function \a validatePayload() and if it fail, shifts for next 1 byte. If
* all validations were successful raise \a packetReceived() event. \n \n
* * PIPacketExtractor::Footer \n
* This algorithm similar to previous, but instead of \a header() first validate
* \a footer() at after \a payloadSize() bytes with function \a validateFooter(). \n \n
* * PIPacketExtractor::HeaderAndFooter \n
* Wait for at least \a header() bytes + \a footer() bytes, then validate
* header with virtual function \a validateHeader() and if it fail, shifts
* for next 1 byte. If header is successfully validated check footer with
* function \a validateFooter() and if it fail, shifts footer position for
* next 1 byte. Then validate payload and if it fail, search header again,
* starts from next byte of previous header. If all validations were successful
* raise \a packetReceived() event. \n \n
* * PIPacketExtractor::Size \n
* Wait for at least \a packetSize() bytes, then validate packet with function
* \a validatePayload() and if it fail, shifts for next 1 byte. If validating
* was successfull raise \a packetReceived() event. \n \n
* * PIPacketExtractor::Timeout \n
* Wait for first read, then read for \a timeout() milliseconds and raise
* \a packetReceived() event. \n
*
* \section PIPacketExtractor_control Control validating
* There are three parameters:
* * header content
* * header size
* * payload size
*
* Extractor can detect packet with compare your header with readed data.
* It is default implementation of function \a packetHeaderValidate().
* If header validating passed, function \a packetValidate() will be called.
* If either of this function return \b false extractor shifts by one byte
* and takes next header. If both functions returns \b true extractor shifts
* by whole packet size.
* \image html packet_detection.png
*
* */
REGISTER_DEVICE(PIPacketExtractor)
PIPacketExtractor::PIPacketExtractor(PIIODevice * device_, PIPacketExtractor::SplitMode mode) {
construct();
setDevice(device_);
setSplitMode(mode);
}
void PIPacketExtractor::construct() {
func_header = nullptr;
func_footer = nullptr;
func_payload = nullptr;
setPayloadSize(0);
setTimeout(100_ms);
#ifdef MICRO_PIP
setThreadedReadBufferSize(512);
#else
setThreadedReadBufferSize(64_KiB);
#endif
setDevice(nullptr);
setSplitMode(None);
missed = footerInd = 0;
header_found = false;
}
void PIPacketExtractor::propertyChanged(const char *) {
mode_ = (SplitMode)(property("splitMode").toInt());
dataSize = property("payloadSize").toInt();
src_header = property("header").toByteArray();
src_footer = property("footer").toByteArray();
time_ = property("timeout").toSystemTime();
}
ssize_t PIPacketExtractor::readDevice(void * read_to, ssize_t max_size) {
if (dev) return dev->read(read_to, max_size);
return -1;
}
ssize_t PIPacketExtractor::writeDevice(const void * data, ssize_t max_size) {
if (dev) return dev->write(data, max_size);
return -1;
}
void PIPacketExtractor::setDevice(PIIODevice * device_) {
dev = device_;
}
ssize_t PIPacketExtractor::bytesAvailable() const {
if (dev)
return dev->bytesAvailable();
else
return 0;
}
void PIPacketExtractor::setPayloadSize(int size) {
setProperty("payloadSize", size);
dataSize = size;
}
void PIPacketExtractor::setHeader(const PIByteArray & data) {
setProperty("header", data);
src_header = data;
}
void PIPacketExtractor::setFooter(const PIByteArray & data) {
setProperty("footer", data);
src_footer = data;
}
int PIPacketExtractor::validateHeader(const uchar * src, const uchar * rec, int size) {
if (func_header) return func_header(src, rec, size);
for (int i = 0; i < size; ++i) {
if (src[i] != rec[i]) return -1;
}
return dataSize;
}
bool PIPacketExtractor::validateFooter(const uchar * src, const uchar * rec, int size) {
if (func_footer) return func_footer(src, rec, size);
for (int i = 0; i < size; ++i) {
if (src[i] != rec[i]) return false;
}
return true;
}
bool PIPacketExtractor::validatePayload(const uchar * rec, int size) {
if (func_payload) return func_payload(rec, size);
return true;
}
bool PIPacketExtractor::threadedRead(const uchar * readed, ssize_t size_) {
// piCoutObj << "readed" << size_;
int ss;
tmpbuf.append(readed, size_);
switch (mode_) {
case PIPacketExtractor::None:
tmpbuf.clear();
if (validatePayload(readed, size_)) {
packetReceived(readed, size_);
} else {
missed += size_;
}
break;
case PIPacketExtractor::Header:
if (src_header.isEmpty()) return PIIODevice::threadedRead(readed, size_);
while (tmpbuf.size() >= src_header.size()) {
if (!header_found) {
packetSize_pending = validateHeader(src_header.data(), tmpbuf.data(), src_header.size_s());
while (packetSize_pending < 0) {
tmpbuf.pop_front();
++missed;
if (tmpbuf.size() < src_header.size()) return PIIODevice::threadedRead(readed, size_);
packetSize_pending = validateHeader(src_header.data(), tmpbuf.data(), src_header.size_s());
}
header_found = true;
}
if (tmpbuf.size_s() < src_header.size_s() + packetSize_pending) return PIIODevice::threadedRead(readed, size_);
if (!validatePayload(tmpbuf.data(src_header.size_s()), packetSize_pending)) {
tmpbuf.pop_front();
++missed;
header_found = false;
continue;
}
packetReceived(tmpbuf.data(), src_header.size_s() + packetSize_pending);
tmpbuf.remove(0, src_header.size_s() + packetSize_pending);
header_found = false;
}
break;
case PIPacketExtractor::Footer:
if (src_footer.isEmpty()) return PIIODevice::threadedRead(readed, size_);
ss = src_footer.size_s() + dataSize;
while (tmpbuf.size_s() >= ss) {
while (!validateFooter(src_footer.data(), tmpbuf.data(dataSize), src_footer.size_s())) {
tmpbuf.pop_front();
++missed;
if (tmpbuf.size_s() < ss) return PIIODevice::threadedRead(readed, size_);
}
if (!validatePayload(tmpbuf.data(), dataSize)) {
tmpbuf.pop_front();
++missed;
continue;
}
packetReceived(tmpbuf.data(), ss);
tmpbuf.remove(0, ss);
}
break;
case PIPacketExtractor::HeaderAndFooter:
ss = src_header.size_s() + src_footer.size_s();
while (tmpbuf.size_s() >= ss && ss > 0) {
if (!header_found) {
if (tmpbuf.size_s() < ss) return PIIODevice::threadedRead(readed, size_);
while (validateHeader(src_header.data(), tmpbuf.data(), src_header.size_s()) < 0) {
tmpbuf.pop_front();
++missed;
if (tmpbuf.size_s() < ss) return PIIODevice::threadedRead(readed, size_);
}
header_found = true;
footerInd = src_header.size_s();
} else {
if (tmpbuf.size_s() < footerInd + src_footer.size_s()) return PIIODevice::threadedRead(readed, size_);
while (!validateFooter(src_footer.data(), tmpbuf.data(footerInd), src_footer.size_s())) {
++footerInd;
if (tmpbuf.size_s() < footerInd + src_footer.size_s()) return PIIODevice::threadedRead(readed, size_);
}
// piCout << "footer found at" << footerInd;
header_found = false;
if (!validatePayload(tmpbuf.data(src_header.size_s()), footerInd - src_header.size_s())) {
tmpbuf.pop_front();
++missed;
continue;
}
packetReceived(tmpbuf.data(), footerInd + src_footer.size_s());
tmpbuf.remove(0, footerInd + src_footer.size_s());
footerInd = src_header.size_s();
}
}
break;
case PIPacketExtractor::Size:
if (dataSize <= 0) {
tmpbuf.clear();
return PIIODevice::threadedRead(readed, size_);
}
while (tmpbuf.size_s() >= dataSize) {
if (!validatePayload(tmpbuf.data(), dataSize)) {
tmpbuf.pop_front();
++missed;
continue;
}
packetReceived(tmpbuf.data(), dataSize);
tmpbuf.remove(0, dataSize);
}
break;
case PIPacketExtractor::Timeout:
tmpbuf.append(dev->readForTime(time_));
if (tmpbuf.size() > 0) {
packetReceived(tmpbuf.data(), tmpbuf.size());
tmpbuf.clear();
}
break;
};
return PIIODevice::threadedRead(readed, size_);
}
PIString PIPacketExtractor::constructFullPathDevice() const {
return "";
}
bool PIPacketExtractor::openDevice() {
if (dev) {
if (!dev->isOpened()) return dev->open();
return dev->isOpened();
}
return false;
}
bool PIPacketExtractor::closeDevice() {
if (dev) {
if (dev->isOpened()) return dev->close();
return !dev->isOpened();
}
return false;
}
PIIODevice::DeviceInfoFlags PIPacketExtractor::deviceInfoFlags() const {
if (dev) return dev->infoFlags();
return 0;
}