/* PIP - Platform Independent Primitives PIPackedTCP 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 . */ #include "pipackedtcp.h" #include "piethernet.h" #include "piliterals.h" /** \class PIPackedTCP pipackedtcp.h * \brief * TCP packed channel * * \details * \section PITCP_sec0 Synopsis * %PIEthernet designed to work with IPv4 network via two protocols: * UDP and TCP. This class allow you send and receive packets to/from * another computer through network. Also it supports broadcast and * multicast extensions. * * */ PIPackedTCP::PIPackedTCP(Role role, const PINetworkAddress & addr): m_role(role) { setMode(PIIODevice::ReadWrite); packer.setCryptEnabled(false); CONNECTL(&packer, packetReceiveEvent, [this](PIByteArray & data) { PIMutexLocker ml(rec_mutex); rec_queue.enqueue(data); }); init(); setAddress(addr); } PIPackedTCP::~PIPackedTCP() { stopAndWait(); if (client) client->stopAndWait(); if (eth) eth->stopAndWait(); piDeleteSafety(eth); } void PIPackedTCP::setAddress(const PINetworkAddress & addr) { m_addr = addr; setPath(m_addr.toString()); } bool PIPackedTCP::isConnected() const { if (m_role == Client) { return eth->isConnected(); } else { if (client) return client->isConnected(); } return false; } bool PIPackedTCP::isConnecting() const { if (m_role == Client) { return eth->isConnecting(); } else { if (client) return client->isConnecting(); } return false; } void PIPackedTCP::init() { if (client) client->stopAndWait(); if (eth) eth->stopAndWait(); piDeleteSafety(eth); eth = new PIEthernet(m_role == Client ? PIEthernet::TCP_Client : PIEthernet::TCP_Server); if (m_role == Client) { eth->setReopenTimeout(100_ms); packer.assignDevice(eth); CONNECTL(eth, connected, [this]() { packer.clear(); connected(); }); CONNECTL(eth, disconnected, [this](bool) { packer.clear(); eth->connect(path(), true); disconnected(); }); } else { CONNECTL(eth, newConnection, [this](PIEthernet * c) { if (client) client->stopAndWait(); piDeleteSafety(client); client = c; // piCout << "Server connected" << client; packer.assignDevice(client); CONNECTL(client, disconnected, [this](bool) { // packer.assignDevice(nullptr); WTF? packer.clear(); disconnected(); }); client->startThreadedRead(); connected(); }); } } PIIODevice::DeviceInfoFlags PIPackedTCP::deviceInfoFlags() const { return PIIODevice::Reliable; } PIString PIPackedTCP::constructFullPathDevice() const { return PIString(m_role == Client ? "client" : "server") + ":" + path(); } void PIPackedTCP::configureFromFullPathDevice(const PIString & full_path) { PIStringList pl = full_path.split(":"); if (pl.size() >= 1) { PIString p = pl[0].toLowerCase().left(1); if (p == "c") m_role = Client; if (p == "s") m_role = Server; init(); } PINetworkAddress addr("0.0.0.0", 13362); if (pl.size() >= 2) { if (pl[1].isNotEmpty()) addr.setIP(pl[1]); } if (pl.size() >= 3) { if (pl[2].isNotEmpty()) addr.setPort(pl[2].toInt()); } setAddress(addr); } ssize_t PIPackedTCP::readDevice(void * read_to, ssize_t max_size) { PIMutexLocker ml(rec_mutex); if (rec_queue.isNotEmpty()) { auto d = rec_queue.dequeue(); auto sz = piMin(max_size, d.size_s()); if (read_to) memcpy(read_to, d.data(), sz); return sz; } return 0; } ssize_t PIPackedTCP::writeDevice(const void * data, ssize_t max_size) { if (!isConnected()) return 0; packer.send(PIByteArray(data, max_size)); // piCout << m_role << "write" << eth; return max_size; /*if (m_role == Client) { return eth->write(data, max_size); } else { if (client) return client->write(data, max_size); }*/ } bool PIPackedTCP::openDevice() { if (m_role == Client) { if (eth->isConnected()) return true; if (eth->isConnecting()) return false; packer.clear(); bool ret = eth->connect(path(), false); eth->startThreadedRead(); return ret; } else { return eth->listen(path(), false); } return false; } bool PIPackedTCP::closeDevice() { if (client) { client->close(); client->stopAndWait(); piDeleteSafety(client); // packer.assignDevice(nullptr); WTF? } return eth->close(); }