NetUDP bietet einen UDP -Socket, der Datagramm senden und empfangen kann.
Diese Bibliothek befindet sich im Wartungsmodus. Dies bedeutet, dass nur kritische Fehler behoben werden. Es werden keine neuen Funktionen hinzugefügt. Wenn Sie diese Bibliothek übernehmen möchten, tun Sie dies bitte.
Die Bibliothek hat einen riesigen Designfehler, eine flankige API und Tests. Es war ein Teil meines Experiments, als ich jung war, und ich habe viel daraus gelernt. Ich möchte die API nicht reparieren, denn wenn Sie QT verwenden, sollten Sie sich nicht auf Bibliothek Dritter verlassen, sondern einfach QUdpSocket verwenden.
Halten Sie Ihre Abhängigkeiten so klein wie möglich, sodass die Wartbarkeit einfacher ist.
Die beiden Hauptklassen, die aus der Schachtel funktionieren, sind Socket und RecycledDatagram . Erstellen Sie einfach einen Server, starten Sie ihn. Senden und empfangen Sie dann Datagramme. Der Server kann die Multicast -Gruppe beitreten, um Multicast -Pakete zu empfangen.
Der Socket verwendet einen Worker , der auf separatem Faden oder im Hauptfaden ausgeführt wird.
Jede Datagrammzuweisung wird in std::shared_ptr<Datagram> gespeichert. Dies erlaubt es, die bereits später zugewiesene Datagramm -Objektstruktur wiederzuverwenden, ohne etwas neu zu vermitteln.
ISocket kann geerbt werden, um eine Socket ohne Funktionalität darzustellen.
Socket und Worker können geerbt werden, um eine benutzerdefinierte Kommunikation zwischen Server und Worker zu implementieren. Zum Beispiel Senden benutzerdefinierter Objekte, die in Worker -Thread serialisiert/deserialisiert werden können.
Datagram kann bei Bedarf ein benutzerdefinierter Datencontainer vererbt werden. Zum Beispiel, wenn Daten bereits in einer Struktur serialisiert sind. Wenn Sie einen Verweis auf diese Struktur innerhalb des Datagram geben, vermeiden Sie eine Kopie zum RecycledDatagram .
Ein grundlegender Client/Socket finden Sie in examples/EchoClientServer.cpp .
In diesem Beispiel wird gezeigt, wie ein Server erstellt wird, der Datagramm an 127.0.0.1 in Port 9999 sendet.
# include < NetUdp/NetUdp.hpp >
int main ( int argc, char * argv[])
{
QCoreApplication app (argc, argv);
netudp::Socket server;
server. start ();
const std::string data = " Dummy Data " ;
server. sendDatagram (data. c_str (), data. length ()+ 1 , " 127.0.0.1 " , 9999 );
return QCoreApplication::exec ();
}Das Datagramm wird von einem vom Betriebssystem ausgewählten zufälligen Port ausgewählt. Es kann explizit durch telefonische
setTxPort(uint16_t)angegeben werden.Wenn der Socket auch Datagramm empfängt (dh
inputEnabledist wahr undsetRxPort), wird der RX -Port verwendet. Um dieses Standardverhalten zu ändern, rufen SiesetSeparateRxTxSockets(true)an.
Dieses Beispiel zeigt, wie ein Paket unter der Adresse 127.0.0.1 auf Port 9999 empfangen wird.
# include < NetUdp/NetUdp.hpp >
int main ( int argc, char * argv[])
{
QCoreApplication app (argc, argv);
netudp::Socket client;
client. start ( " 127.0.0.1 " , 9999 );
QObject::connect (&client, &netudp::Socket::sharedDatagramReceived,
[]( const netudp::SharedDatagram& d)
{
qInfo ( " Rx : %s " , reinterpret_cast < const char *>(d-> buffer ()));
});
return QCoreApplication::exec ();
} Fehler können über socketError(int error, QString description) Signale beobachtet werden. Wenn der Socket nicht binden oder wenn etwas passiert ist, startet der Arbeiter einen Wachhund -Timer, um den Sockel neu zu starten.
Die Standard -Neustartzeit wird auf 5 Sekunden gesetzt, kann jedoch über eine watchdogPeriod -Eigenschaft geändert werden. Das Eigentum wird in Millisekunden ausgedrückt.
Wenn der interne Socket an eine Schnittstelle mit einem Port begrenzt ist, erhält der Worker eingehendes Datagramm. Um zu vermeiden, dass diese Datagramm in Socket empfangen werden, rufen Sie setInputEnabled(false) .
multicastGroups ist die Liste der angehaltenen Multicast -Adressen. Um die Multicast Group Call joinMulticastGroup(QString) , leaveMulticastGroup(QString) , leaveAllMulticastGroups anzuschließen.multicastListeningInterfaces : Stellen Sie die Schnittstellen ein, auf der der Sockel multicastGroups hört. Standardmäßig werden alle Schnittstellen angehört. Verwenden Sie joinMulticastInterface , leaveMulticastInterface , leaveAllMulticastInterfaces und isMulticastInterfacePresent .multicastLoopback -Steuerung Wenn das Multicast -Datagramm im System läuft. Unter Windows sollte es auf der Empfängerseite eingestellt werden. Auf UNIX -Systemen sollte es auf der Absenderseite eingestellt werden. multicastOutgoingInterfaces : Ausgeschaltete Schnittstellen für Multicast -Paket. Wenn nicht angegeben, geht das Paket standardmäßig an alle Schnittstellen, um ein Plug -and -Play -Erlebnis bereitzustellen. Innen verfolgen der Socket mehrere Informationen, um eine Vorstellung davon zu haben, was los ist.
isBounded GEBEN SIE, WERT DER SOCKET derzeit für eine Netzwerkschnittstelle gebunden ist.*xBytesPerSeconds ist ein Durchschnittswert aller in der letzten Sekunde empfangenen/gesendeten Bytes. Dieser Wert wird alle Sekunden aktualisiert. * can be replaced by t and r*xBytesTotal TOTAL ERHALTEN/SEISE BYTES Seit Start erhalten. * can be replaced by t and r*xPacketsPerSeconds ist ein Durchschnittswert aller in der letzten Sekunde empfangenen/gesendeten Pakete. Dieser Wert wird alle Sekunden aktualisiert. * can be replaced by t and r*xPacketsTotal Total empfangen/gesendete Pakete seit Start. * can be replaced by t and r Diese Eigenschaft kann mit clearRxCounter / clearTxCounter / clearCounters gelöscht werden.
Wenn Sie eine der folgenden Funktionen aufrufen, passt ein memcpy in einem RecycledDatagram ein.
virtual bool sendDatagram ( const uint8_t * buffer, const size_t length, const QHostAddress& address, const uint16_t port, const uint8_t ttl = 0 );
virtual bool sendDatagram ( const uint8_t * buffer, const size_t length, const QString& address, const uint16_t port, const uint8_t ttl = 0 );
virtual bool sendDatagram ( const char * buffer, const size_t length, const QHostAddress& address, const uint16_t port, const uint8_t ttl = 0 );
virtual bool sendDatagram ( const char * buffer, const size_t length, const QString& address, const uint16_t port, const uint8_t ttl = 0 ); Um eine nutzlose Speicherkopie zu vermeiden, wird empfohlen, ein Datagramm aus Socket -Cache mit makeDatagram(const size_t length) abzurufen. Verwenden Sie dann dieses netudp::SharedDatagram um Daten zu serialisieren. Und rufen Sie an:
virtual bool sendDatagram (std::shared_ptr<Datagram> datagram, const QString& address, const uint16_t port, const uint8_t ttl = 0 );
virtual bool sendDatagram (std::shared_ptr<Datagram> datagram); Wenn Sie nicht durch Socket -Verhalten zufrieden sind oder wenn Sie Socket ohne Abhängigkeit von QtNetwork verspotten möchten. Es ist möglich, ISocket zu erweitern, um die grundlegende Funktionalität zu verwenden.
isRunning / isBounded .Sie müssen überschreiben:
bool start() : Starten Sie den Socket. Der automatische Neustart, um vom Fehler zu überleben, wird erwartet. Vergessen Sie nicht, ISocket::start am Anfang.bool stop() : Stoppen Sie den Sockel. Löschen Sie alle laufenden Aufgaben, leerer Cache, Puffer usw. ... Vergessen Sie nicht, ISocket::stop beim Anfang. Um eine maximale Reinigung zu gewährleisten, stoppen Sie immer alle, selbst wenn das Anhalten eines Teils fehlschlägt.joinMulticastGroup(const QString& groupAddress) : Implementierung zur Beitritt zu einer Multicast -Gruppe. Vergessen Sie nicht, ISocket::joinMulticastGroup anzurufen.leaveMulticastGroup(const QString& groupAddress) : Implementierung, um eine Multicast -Gruppe zu hinterlassen. Vergessen Sie nicht, ISocket::leaveMulticastGroup . # include < NetUdp/ISocket.hpp >
class MyAbstractSocket : netudp::ISocket
{
Q_OBJECT
public:
MyAbstractSocket (QObject* parent = nullptr ) : netudp::ISocket(parent) {}
public Q_SLOTS:
bool start () override
{
if (! netudp::ISocket::start ())
return false ;
// Do your business ...
return true ;
}
bool stop () override
{
auto stopped = netudp::ISocket::stop ()
// Do your business ...
return stopped;
}
bool joinMulticastGroup ( const QString& groupAddress) override
{
// Join groupAddress ...
return true ;
}
bool leaveMulticastGroup ( const QString& groupAddress) override
{
// Leave groupAddress ...
return true ;
}
} Socket und Worker arbeiten hauptsächlich zu zweit. Wenn Sie also einen überschreiben, ist es oft sinnvoll, den anderen außer Kraft zu setzen.
Gründe, Worker außer Kraft zu setzen:
Datagram Gründe, Socket zu überschreiben
Worker .Datagram . Durch die Verwendung eines benutzerdefinierten Datagram kann die Speicherkopie je nach Anwendung reduziert werden.
Worker anpassen.Socket::sendDatagram(SharedDatagram, ...) damit.Socket an, um sie beim Aufrufen mit Socket::sendDatagram(const uint8_t*, ...) zu verwenden. Eine memcpy wird passieren. Verwenden Sie also kein benutzerdefiniertes Datagram zu diesem Zweck. # include < NetUdp/Datagram.hpp >
class MyDatagram : netudp::Datagram
{
uint8_t * myBuffer = nullptr ;
size_t myLength = 0 ;
public:
uint8_t * buffer () { return myBuffer; }
const uint8_t * buffer () const { return myBuffer; }
size_t length () const { return myLength; }
}; Wenn Sie von SocketWorker erben, können Sie überschreiben:
bool isPacketValid(const uint8_t* buffer, const size_t length) const : Auf dem Empfang eines Datagramms aufgerufen. Überprüfen Sie, ob ein Paket je nach Protokoll gültig ist. Die Standardimplementierung gibt einfach true zurück. Sie können einen CRC -Scheck oder ähnliches hinzufügen. Die Rückgabe von FALSH hier erhöht den rxInvalidPacketTotal Zähler in Socket .void onReceivedDatagram(const SharedDatagram& datagram) : Jedes Mal, wenn ein gültiges Datagramm ankommt. Standard -Implementierung EMIT receivedDatagram -Signal. Überschreiben Sie diese Funktion, um ein benutzerdefiniertes Messaging -System oder eine benutzerdefinierte Deserialisierung hinzuzufügen.std::shared_ptr<Datagram> makeDatagram(const size_t length) : Erstellen Sie benutzerdefiniertes Datagram für RX.Worker implementieren, rufen Sie void onSendDatagram(const SharedDatagram& datagram) an, um ein Datagramm an das Netzwerk zu senden.SocketWorker von QObject erbt. Verwenden Sie daher Q_OBJECT -Makro, um benutzerdefinierte Signale zu generieren.Beispiel:
# include < NetUdp/Worker.hpp >
class MySocketWorker : netudp::Worker
{
Q_OBJECT
public:
MySocketWorker (QObject* parent = nullptr ) : netudp::SocketWorker(parent) {}
public Q_SLOTS:
bool std::unique_ptr<SocketWorker> createWorker () override
{
auto myWorker = std::make_unique<MyWorker>();
// Init your worker with custom stuff ...
// Even keep reference to MyWorker* if you need later access
// It's recommended to communicate via signals to the worker
// Connect here ...
return std::move (myWorker);
}
// This is called before creating a SharedDatagram and calling onDatagramReceived
bool isPacketValid ( const uint8_t * buffer, const size_t length) const override
{
// Add your checks, like header, fixed size, crc, etc...
return buffer && length;
}
void onDatagramReceived ( const SharedDatagram& datagram) override
{
// Do your business ...
// This super call is optionnal. If not done Socket will never trigger onDatagramReceived
netudp::SocketWorker::onDatagramReceived (datagram);
}
std::shared_ptr<Datagram> makeDatagram ( const size_t length) override
{
// Return your custom diagram type used for rx
return std::make_shared<MyDiagram>(length);
}
}Das Anpassen von Arbeitern ist größtenteils sinnvoll, wenn es in einem separaten Thread ausgeführt wird. Andernfalls wird es keinen Leistungsschub geben. Vergessen Sie nicht
Socket::setUseWorkerThread(true)anzurufen.
Wenn Sie von Socket von Socket erben, können Sie überschreiben:
bool std::unique_ptr<SocketWorker> createWorker() const : Erstellen Sie einen benutzerdefinierten Arbeiter.void onDatagramReceived(const SharedDatagram& datagram) : Datagram bearbeiten. Standard -Implementierung emittieren Sie datagramReceived -Signalestd::shared_ptr<Datagram> makeDatagram(const size_t length) : Erstellen Sie benutzerdefiniertes Datagram , das in Socket::sendDatagram(const uint8_t*, ...) verwendet wird.Socket von QObject erbt. Verwenden Sie daher Q_OBJECT -Makro, um benutzerdefinierte Signale zu generieren.Beispiel:
# include < NetUdp/Socket.hpp >
class MySocket : netudp::Socket
{
Q_OBJECT
public:
MySocket (QObject* parent = nullptr ) : netudp::Socket(parent) {}
public Q_SLOTS:
bool std::unique_ptr<Worker> createWorker () override
{
auto myWorker = std::make_unique<MyWorker>();
// Init your worker with custom stuff ...
// Even keep reference to MyWorker* if you need later access
// It's recommended to communicate via signals to the worker
// Connect here ...
return std::move (myWorker);
}
void onDatagramReceived ( const SharedDatagram& datagram) override
{
// Do your business ...
// This super call is optionnal. If not done Socket will never trigger datagramReceived signal
netudp::Socket::onDatagramReceived (datagram);
}
std::shared_ptr<Datagram> makeDatagram ( const size_t length) override
{
// Return your custom diagram type used for tx
return std::make_shared<MyDiagram>(length);
}
} Dieses Beispiel zeigt ein Echo zwischen einem Server und einem Client. Socket Senden Sie ein Paket an einen Client, der Client antwortet dasselbe Paket. Ctrl+C um zu beenden.
$ > NetUdp_EchoClientServer --help
Options:
- ? , -h, --help Displays this help.
-t Make the worker live in a different thread. Default false
-s, --src < port > Port for rx packet. Default " 11111 " .
-d, --dst < port > Port for tx packet. Default " 11112 " .
--src-addr < ip > Ip address for server. Default " 127.0.0.1 "
--dst-addr < ip > Ip address for client. Default " 127.0.0.1 "
$ > NetUdp_EchoClientServer
> app: Init application
> server: Set Rx Address to 127.0.0.1
> server: Set Rx Port to 11111
> client: Set Rx Address to 127.0.0.1
> client: Set Rx Port to 11112
> app: Start application
> client: Rx : Echo 0
> server: Rx : Echo 0
> client: Rx : Echo 1
> server: Rx : Echo 1
> client: Rx : Echo 2
> server: Rx : Echo 2
> ... Dieses Beispiel ist auch in 2 Beispiele eingebrochen: NetUdp_EchoClient & NetUdp_EchoServer .
Zeigen Sie, wie Sie Multicast IP -Gruppe beitreten. Senden Sie ein Paket und lesen Sie es über Loopback zurück.
$ > NetUdp_EchoMulticastLoopback --help
Options:
- ? , -h, --help Displays this help.
-t Make the worker live in a different thread. Default
false
-p Print available multicast interface name
-s, --src < port > Port for rx packet. Default " 11111 " .
-i, --ip < ip > Ip address of multicast group. Default " 239.0.0.1 "
--if, --interface < if > Name of the iface to join. Default is os dependent
netudp::registerQmlTypes(); sollte im Hauptberuf aufgerufen werden, um QML -Typen zu registrieren. In diesem Beispiel wird angezeigt, wie ein Unicast -Datagramm als Zeichenfolge an 127.0.0.1:9999 gesendet wird. Vergessen Sie nicht, den Socket zu starten, bevor Sie Nachrichten senden.
import QtQuick 2.0
import QtQuick . Controls 2.0
import NetUdp 1.0 as NetUdp
Button
{
text : "send unicast"
onClicked : ( ) = > socket . sendDatagram ( {
address : "127.0.0.1" ,
port : 9999 ,
data : "My Data"
// Equivalent to 'data: [77,121,32,68,97,116,97]'
} )
NetUdp . Socket
{
id: socket
Component . onCompleted : ( ) = > start ( )
}
}In diesem Beispiel wird angezeigt, wie das Datagramm empfangen wird. Vergessen Sie nicht, sich eine Adresse und einen Port anzuhören. Das Datagramm wird immer als Zeichenfolge empfangen. Es kann leicht dekodiert werden, um ein Byte -Array zu manipulieren.
import NetUdp 1.0 as NetUdp
NetUdp . Socket
{
onDatagramReceived : function ( datagram )
{
console . log ( `datagram : ${ JSON . stringify ( datagram ) } ` )
console . log ( `datagram.data (string) : " ${ datagram . data } "` )
let byteArray = [ ]
for ( let i = 0 ; i < datagram . data . length ; ++ i )
byteArray . push ( datagram . data . charCodeAt ( i ) )
console . log ( `datagram.data (bytes): [ ${ byteArray } ]` )
console . log ( `datagram.destinationAddress : ${ datagram . destinationAddress } ` )
console . log ( `datagram.destinationPort : ${ datagram . destinationPort } ` )
console . log ( `datagram.senderAddress : ${ datagram . senderAddress } ` )
console . log ( `datagram.senderPort : ${ datagram . senderPort } ` )
console . log ( `datagram.ttl : ${ datagram . ttl } ` )
}
Component . onCompleted : ( ) = > start ( "127.0.0.1" , 9999 )
}Senden Sie Multicast -Datagrammarbeit fast gleich wie bei Unicast. Der einzige Unterschied besteht darin, dass Sie die Schnittstelle über die Daten steuern.
import NetUdp 1.0 as NetUdp
NetUdp . Socket
{
id : socket
// A Packet will be send to each interface
// The socket monitor for interface connection/disconnection
multicastOutgoingInterfaces : [ "lo" , "eth0" ]
// Required in unix world if you want loopback on the same system
multicastLoopback : true
Component . onCompleted : ( ) => start ( )
}Senden Sie dann Daten wie in Unicast:
socket . sendDatagram ( {
address : "239.1.2.3" ,
port : 9999 ,
data : "My Data"
} )Abonnieren Sie die Multicast -Gruppe, um es zu erhalten, und wählen Sie für welche Schnittstellen.
import NetUdp 1.0 as NetUdp
NetUdp . Socket
{
multicastGroups : [ "239.1.3.4" ]
multicastListeningInterfaces : [ "lo" , "eth0" ]
// Required in the windows world if you want loopback on the same system
multicastLoopback : true
onDatagramReceived : ( datagram ) = > console . log ( `datagram : ${ JSON . stringify ( datagram ) } ` )
// Listen port 9999
Component . onCompleted : ( ) = > start ( 12999934 )
}Diese Bibliothek bietet auch ein Werkzeugobjekt, das jede QMLS -Funktionalität demonstriert. Dies ist für schnelles Debug oder Testfunktionalitäten vorgesehen, wenn die Benutzeroberfläche noch nicht erstellt wird.
Abhängigkeiten -Diagramme können mit:
mkdir -p build && cd build cmake --graphviz=dependencies.dot .. dot -Tsvg -o ../docs/dependencies.svg dependencies.dot -Gbgcolor=transparent -Nfillcolor=white -Nstyle=filled
pip install cmakelang ) NETUDP verwenden automatische Formatierung für cpp , cmake . Die scripts enthalten Helfer -Skript. Es wird empfohlen, automatisch in IDE zu formatieren.
cd scripts
./clangformat.sh
./cmakeformat.sh
? Netudp -> netudp? netudp-> netudp ➖ SPDLOG-Abhängigkeit im Geschmack von QCDebug/QCWARNING entfernen ➕ Abhängigkeiten über CPM ♻️ Arbeiter: Schnittstelle-> IFACE, um Konflikte mit MSVC # Define Interface Struct https://stackoverflow.com/questions/questions/what-is-the-the-the-the-naktions/what-is-/what-is-/what-is--- WorkerPrivate ♻️ Pimpl SocketPrivate ♻️ Pimpl recyceltDatagrprivate? Machen Sie recycler privat, da alle Recycler in den Pimpl ⚡️ netudp_enable_unity_build bewegt wurden? InterfaceProvider: Verwenden Sie Steady_clock anstelle von System, um Rollback zu vermeiden? Drucken Sie den Befehl Build im von CMake Update ReadMe mit Abhängigkeiten -Diagramme
? Fügen Sie fehlende QelapsedTimer -Header in Arbeiter ein
? Verwenden Sie Rohzeiger für Worker & Worker -Thread. ? ️ Dies sollte das Problem beheben, wenn der Port nicht vollständig veröffentlicht wurde.
NETUDP_ENABLE_PCH , NETUDP_ENABLE_EXAMPLES , NETUDP_ENABLE_TESTSresize .multicastOutgoingInterfaces anstelle von multicastInterfaceName ein. Wenn multicastOutgoingInterfaces leere Pakete an allen Schnittstellen senden werden.multicastListenOnAllInterfaces und erstellen Sie es standardmäßig, wenn multicastListeningInterfaces leer ist.