يوفر NetuDP مقبس UDP الذي يمكنه إرسال واستقبال بيانات البيانات.
هذه المكتبة في وضع الصيانة. وهذا يعني أنه سيتم إصلاح الأخطاء الهامة فقط. لن تتم إضافة ميزات جديدة. إذا كنت ترغب في تولي هذه المكتبة ، فيرجى القيام بذلك.
تحتوي المكتبة على بعض عيب التصميم الضخم ، واجهة برمجة التطبيقات (API) والاختبارات. لقد كان جزءًا من تجربتي عندما كنت صغيراً ، وتعلمت الكثير منه. لا أرغب في إصلاح واجهة برمجة التطبيقات ، لأنه عند استخدام QT ، يجب ألا تعتمد على مكتبة الطرف الثالث مثل ، ولكن ببساطة استخدم QUdpSocket .
تبقيك التبعيات صغيرة قدر الإمكان ، لذلك فإن الصيانة أسهل.
الفئتان الرئيسيتان اللذان يعملان خارج الصندوق هما Socket RecycledDatagram . ببساطة إنشاء خادم ، ابدأه. ثم إرسال واستقبال البيانات. يمكن للخادم الانضمام إلى مجموعة البث المتعدد لتلقي حزم البث المتعدد.
يستخدم Socket Worker يمكنه تشغيله على مؤشر ترابط منفصل أو في مؤشر ترابط رئيسي.
يتم تخزين كل تخصيص DataGram في std::shared_ptr<Datagram> . هذا يسمح بإعادة استخدام بنية كائن البيانات التي تم تخصيصها بالفعل لاحقًا دون إعادة تخصيص أي شيء.
يمكن أن يكون ISocket موروثة لتمثيل Socket دون أي وظيفة.
يمكن ورث Socket Worker لتنفيذ التواصل المخصص بين الخادم والعامل. على سبيل المثال ، إرسال كائنات مخصصة يمكن التسلسل/إزالة التسلسل في مؤشر ترابط العمال.
يمكن مورث Datagram إذا كانت حاوية بيانات مخصصة إذا لزم الأمر. على سبيل المثال إذا تم بالفعل تسلسل البيانات في هيكل. وضع إشارة إلى هذا الهيكل داخل Datagram تجنب نسخة إلى RecycledDatagram .
يمكن العثور على عميل/مقبس أساسي في examples/EchoClientServer.cpp .
يوضح هذا المثال كيفية إنشاء خادم يرسل Datagram إلى معالجة 127.0.0.1 على المنفذ 9999 .
# 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 ();
}ينبعث بيانات البيانات من منفذ عشوائي يختاره نظام التشغيل. يمكن تحديده بشكل صريح عن طريق استدعاء
setTxPort(uint16_t).إذا كان المقبس يتلقى أيضًا بيانات البيانات (أي
inputEnabledيكون صحيحًا و CallsetRxPort) ، فسيستخدم منفذ RX. لتغيير هذا السلوك الافتراضي استدعاءsetSeparateRxTxSockets(true).
يوضح هذا المثال كيفية تلقي حزمة على العنوان 127.0.0.1 على المنفذ 9999 .
# 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 ();
} يمكن ملاحظة الأخطاء عن طريق socketError(int error, QString description) . إذا فشل المقبس في الربط ، أو في حالة حدوث أي شيء ، سيبدأ العامل مؤقت مراقبة لإعادة تشغيل المقبس.
يتم تعيين وقت إعادة التشغيل الافتراضي على 5 ثوان ولكن يمكن تغييره عبر خاصية watchdogPeriod . يتم التعبير عن العقار بالميلي ثانية.
بشكل افتراضي ، إذا كان المقبس الداخلي محددًا لواجهة مع منفذ ، فسيتلقى Worker بيانات بيانات واردة. لتجنب استلام بيانات البيانات داخل Socket ، استدعاء setInputEnabled(false) .
multicastGroups هي قائمة عناوين البث المتعدد التي يتم الاستماع إليها. للانضمام إلى مجموعة البث المتعدد joinMulticastGroup(QString) ، leaveMulticastGroup(QString) ، leaveAllMulticastGroups .multicastListeningInterfaces : اضبط الواجهات التي يستمع إليها المقبس إلى multicastGroups . بشكل افتراضي ، يتم استشارة جميع الواجهات. استخدم joinMulticastInterface ، و leaveMulticastInterface ، و leaveAllMulticastInterfaces و isMulticastInterfacePresent .multicastLoopback إذا كانت بيانات الإرسال المتعدد تحلق في النظام. على Windows يجب تعيينه على جانب المتلقي. على أنظمة UNIX ، يجب تعيينها على جانب المرسل. multicastOutgoingInterfaces : واجهات صادرة لحزم البث المتعدد. إذا لم يتم تحديدها ، فإن Packet ستذهب إلى جميع الواجهات افتراضيًا لتوفير تجربة توصيل وتشغيل. داخليًا ، تتبع Socket معلومات متعددة للحصول على فكرة عما يجري.
isBounded تشير إلى ما إذا كان المقبس ملزما حاليًا إلى واجهة الشبكة.*xBytesPerSeconds هي قيمة متوسط لجميع البايتات المستلمة/إرسالها في الثانية الأخيرة. يتم تحديث هذه القيمة كل ثواني. * can be replaced by t and r*xBytesTotal TOTART BYTES/SERT SEND منذ البداية. * can be replaced by t and r*xPacketsPerSeconds هي قيمة متوسط لجميع الحزم المستلمة/المرسلة في الثانية الأخيرة. يتم تحديث هذه القيمة كل ثواني. * can be replaced by t and r*xPacketsTotal Total Truled/Sent Sent منذ البدء. * can be replaced by t and r يمكن مسح تلك الممتلكات باستخدام clearRxCounter / clearTxCounter / clearCounters .
عند استدعاء أي من الوظائف التالية ، سيحدث memcpy لـ RecycledDatagram .
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 ); لتجنب نسخة الذاكرة عديمة الفائدة ، يوصى باسترداد بيانات بيانات من Socket مع makeDatagram(const size_t length) . ثم استخدم هذا netudp::SharedDatagram لتسلسل البيانات. والاتصال:
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); إذا لم تكن راضيًا عن سلوك Socket ، أو إذا كنت ترغب في السخرية Socket دون أي اعتماد على QtNetwork . من الممكن تمديد ISocket لاستخدام وظائفها الأساسية.
isRunning / isBounded .تحتاج إلى تجاوز:
bool start() : ابدأ المقبس. من المتوقع إعادة التشغيل التلقائي للبقاء من الخطأ. لا تنسى استدعاء ISocket::start في البداية.bool stop() : أوقف المقبس. امسح كل مهمة التشغيل ، ذاكرة التخزين المؤقت الفارغة ، المخازن المؤقتة ، إلخ ... لا تنسى استدعاء ISocket::stop في البداية. لضمان أقصى قدر من التنظيف ، توقف دائمًا عن كل حتى إذا فشل إيقاف أي جزء.joinMulticastGroup(const QString& groupAddress) : التنفيذ للانضمام إلى مجموعة البث المتعدد. لا تنسى استدعاء ISocket::joinMulticastGroup .leaveMulticastGroup(const QString& groupAddress) : تنفيذ لترك مجموعة البث المتعدد. لا تنسى استدعاء 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 Worker بشكل رئيسي في الزوج ، لذلك إذا كان من المنطقي غالبًا تجاوز الآخر.
أسباب تجاوز Worker :
Datagram مخصصة أسباب تجاوز Socket
Worker المخصصة.Datagram مخصصة. يمكن أن يؤدي استخدام Datagram مخصص إلى تقليل نسخة الذاكرة وفقًا للتطبيق الخاص بك.
Worker .Socket::sendDatagram(SharedDatagram, ...) معها.Socket لاستخدامه عند الاتصال مع Socket::sendDatagram(const uint8_t*, ...) . سيحدث memcpy . لذلك لا تستخدم Datagram مخصصة لهذا الغرض. # 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; }
}; عند الوراثة من SocketWorker ، يمكنك تجاوز:
bool isPacketValid(const uint8_t* buffer, const size_t length) const : يسمى في كل مرة يتم فيها استلام بيانات البيانات. تحقق مما إذا كانت الحزمة صالحة حسب بروتوكولك. التنفيذ الافتراضي مجرد إرجاع صحيح. يمكنك إضافة فحص CRC أو شيء من هذا القبيل. العودة الخاطئة هنا سوف يزيد من عداد rxInvalidPacketTotal في Socket .void onReceivedDatagram(const SharedDatagram& datagram) : يسمى في كل مرة وصول مخطط بيانات صالح. تنفيذ التنفيذ الافتراضي receivedDatagram . تجاوز هذه الوظيفة لإضافة نظام مراسلة مخصصة ، أو إلغاء التخلص المخصص.std::shared_ptr<Datagram> makeDatagram(const size_t length) : قم بإنشاء Datagram مخصص لـ rx.Worker ، فاستدعاء void onSendDatagram(const SharedDatagram& datagram) لإرسال بيانات بيانات إلى الشبكة.SocketWorker يرث من QObject ، لذلك استخدم macro Q_OBJECT لإنشاء إشارات مخصصة.مثال:
# 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);
}
}تخصيص العامل في الغالب أمر منطقي عند تشغيله في موضوع منفصل. وإلا فإنه لن يعطي أي دفعة أداء. لا تنسى استدعاء
Socket::setUseWorkerThread(true).
عند الوراثة من Socket ، يمكنك تجاوز:
bool std::unique_ptr<SocketWorker> createWorker() const : إنشاء عامل مخصص.void onDatagramReceived(const SharedDatagram& datagram) : التعامل مع datagram في هناك. التنفيذ الافتراضي ينبعث إشارات datagramReceivedstd::shared_ptr<Datagram> makeDatagram(const size_t length) : قم بإنشاء Datagram مخصصة سيتم استخدامها في Socket::sendDatagram(const uint8_t*, ...) .Socket يرث من QObject ، لذا استخدم ماكرو Q_OBJECT لإنشاء إشارات مخصصة.مثال:
# 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);
}
} يوضح هذا المثال صدى بين الخادم والعميل. Socket إرسال حزمة إلى عميل ، يرد العميل على نفس الحزمة. Ctrl+C للاستقالة.
$ > 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
> ... هذا المثال ينقسم أيضًا إلى مثالين: NetUdp_EchoClient & NetUdp_EchoServer .
إظهار كيفية الانضمام إلى مجموعة IP MultiCast. أرسل حزمة وقراءتها مرة أخرى عبر Loopback.
$ > 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(); يجب أن يتم استدعاؤها في MAIN لتسجيل أنواع QML. يوضح هذا المثال كيفية إرسال Datagram Unicast كسلسلة إلى 127.0.0.1:9999 . لا تنس أن تبدأ المقبس قبل إرسال أي رسائل.
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 ( )
}
}يوضح هذا المثال كيفية تلقي بيانات البيانات. لا تنسى البدء في الاستماع إلى عنوان ومنفذ. يتم استلام بيانات البيانات دائمًا كسلسلة. يمكن بسهولة فك تشفيرها لمعالجة مجموعة بايت.
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 )
}إرسال بيانات Datagram متعددة البث نفسه تقريبًا مثل الإرسال. الفرق الوحيد هو أنك تتحكم في الواجهة التي تسير عليها البيانات.
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 ( )
}ثم أرسل بيانات كما في Unicast:
socket . sendDatagram ( {
address : "239.1.2.3" ,
port : 9999 ,
data : "My Data"
} )لاستلامها ، اشترك في مجموعة البث المتعدد واختر أي واجهات.
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 )
}توفر هذه المكتبة أيضًا كائن أداة يوضح كل وظيفة QMLS. هذا مخصص للتصحيح السريع ، أو وظائف الاختبار إذا لم يتم بناء واجهة المستخدم بعد.
يمكن إنشاء الرسم البياني التبعيات مع:
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 استخدام التكسير التلقائي لـ cpp ، cmake . تحتوي scripts المجلد على نص مساعد. يوصى بإعداد تنسيق تلقائي داخل IDE.
cd scripts
./clangformat.sh
./cmakeformat.sh
؟ Netudp -> netudp؟ netudp-> netudp ➖ إزالة التبعية spdlog في نكهة QCDebug/QCwarning ➕ إدارة التبعيات عبر CPM ♻ العامل: واجهة-> إذا تجنب الصراع مع msvc # interface struct https://stackoverflow.com/questions/25234203/wwhat-is-hat-hate-treate-treat WorkerPervy ♻ Pimpl SocketPrivate ♻ Pimpl RecycledDataGrampRiperive؟ اجعل recycler Private نظرًا لأن جميع إعادة التدوير تم نقلها داخل pimpl ⚡ netudp_enable_unity_build؟ interfaceprovider: استخدم steady_clock بدلاً من النظام لتجنب التراجع؟ PRINT BUILD Command في CMAKE UPDATE README مع رسم بياني تبعية
؟ قم بتضمين رأس QelapsedTimer المفقود في العامل
؟ استخدم مؤشر RAW للعامل وخيط العمال. ️ هذا يجب أن إصلاح المشكلة عندما لم يتم إطلاق Port بالكامل.
NETUDP_ENABLE_PCH ، NETUDP_ENABLE_EXAMPLES ، NETUDP_ENABLE_TESTSresize .multicastOutgoingInterfaces بدلاً من multicastInterfaceName . إذا كانت multicastOutgoingInterfaces فسيتم إرسال حزم فارغة على كل واجهات.multicastListenOnAllInterfaces وجعلها افتراضيًا عندما يكون multicastListeningInterfaces فارغًا.