NetUDP menyediakan soket UDP yang dapat mengirim dan menerima datagram.
Perpustakaan ini dalam mode pemeliharaan. Ini berarti bahwa hanya bug kritis yang akan diperbaiki. Tidak ada fitur baru yang akan ditambahkan. Jika Anda ingin mengambil alih perpustakaan ini, silakan lakukan.
Perpustakaan memiliki beberapa cacat desain besar, API fliky dan tes. Itu adalah bagian dari pengalaman saya ketika saya masih muda, dan saya belajar banyak darinya. Saya tidak ingin memperbaiki API, karena ketika menggunakan QT Anda tidak boleh mengandalkan perpustakaan pihak ketiga seperti, tetapi hanya menggunakan QUdpSocket .
Menjaga ketergantungan Anda sekecil mungkin, sehingga pemeliharaan lebih mudah.
Dua kelas utama yang bekerja di luar kotak adalah Socket dan RecycledDatagram . Cukup buat server, mulai. Kemudian kirim dan terima datagram. Server dapat bergabung dengan grup multicast untuk menerima paket multicast.
Socket menggunakan Worker yang dapat berjalan di utas terpisah atau di utas utama.
Setiap alokasi datagram disimpan dalam std::shared_ptr<Datagram> . Ini memungkinkan untuk menggunakan kembali struktur objek Datagram yang sudah dialokasikan nanti tanpa merealokasi apa pun.
ISocket dapat diwarisi untuk mewakili Socket tanpa fungsionalitas apa pun.
Socket dan Worker dapat diwarisi untuk mengimplementasikan komunikasi khusus antara server dan pekerja. Misalnya mengirim objek khusus yang dapat diserialisasi/deserialized di utas pekerja.
Datagram dapat diwarisi jika wadah data khusus jika diperlukan. Misalnya jika data sudah serial dalam struktur. Menempatkan referensi ke struktur itu di dalam Datagram , hindari salinan ke RecycledDatagram .
Klien/soket dasar dapat ditemukan dalam examples/EchoClientServer.cpp .
Contoh ini menunjukkan cara membuat server yang mengirim datagram ke alamat 127.0.0.1 di port 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 ();
}Datagram dipancarkan dari port acak yang dipilih oleh sistem operasi. Ini dapat secara eksplisit ditentukan dengan memanggil
setTxPort(uint16_t).Jika soket juga menerima datagram (IE
inputEnabledadalah benar dan hubungisetRxPort), maka port RX akan digunakan. Untuk mengubah perilaku default ini panggilansetSeparateRxTxSockets(true).
Contoh ini menunjukkan cara menerima paket di alamat 127.0.0.1 di port 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 ();
} Kesalahan dapat diamati melalui socketError(int error, QString description) sinyal. Jika soket gagal mengikat, atau jika terjadi sesuatu, pekerja akan memulai timer pengawas untuk memulai kembali soket.
Waktu restart default diatur ke 5 detik tetapi dapat diubah melalui properti watchdogPeriod . Properti ini dinyatakan dalam milidetik.
Secara default, jika soket internal dibatasi ke antarmuka dengan port, Worker akan menerima datagram yang masuk. Untuk menghindari menerima datagram di dalam Socket , hubungi setInputEnabled(false) .
multicastGroups adalah daftar alamat multicast yang didengarkan. Untuk bergabung dengan multicast group call joinMulticastGroup(QString) , leaveMulticastGroup(QString) , leaveAllMulticastGroups .multicastListeningInterfaces : Atur antarmuka tempat soket mendengarkan multicastGroups . Secara default semua antarmuka didengarkan. Gunakan joinMulticastInterface , leaveMulticastInterface , leaveAllMulticastInterfaces dan isMulticastInterfacePresent .multicastLoopback Jika multicast datagram melingkar dalam sistem. Pada windows itu harus diatur di sisi penerima. Pada sistem UNIX, itu harus diatur di sisi pengirim. multicastOutgoingInterfaces : Antarmuka keluar untuk paket multicast. Jika tidak ditentukan, maka paket akan pergi ke semua antarmuka secara default untuk memberikan pengalaman plug and play. Secara internal trek Socket beberapa informasi untuk memiliki gagasan tentang apa yang sedang terjadi.
isBounded menunjukkan jika soket saat ini diikat ke antarmuka jaringan.*xBytesPerSeconds adalah nilai rata -rata semua byte yang diterima/dikirim dalam detik terakhir. Nilai ini diperbarui setiap detik. * can be replaced by t and r*xBytesTotal Total yang diterima/dikirim byte sejak mulai. * can be replaced by t and r*xPacketsPerSeconds adalah nilai rata -rata semua paket yang diterima/dikirim dalam detik terakhir. Nilai ini diperbarui setiap detik. * can be replaced by t and r*xPacketsTotal Total Paket yang Diterima/Dikirim Sejak Mulai. * can be replaced by t and r Properti tersebut dapat dibersihkan dengan clearRxCounter / clearTxCounter / clearCounters .
Saat memanggil salah satu fungsi berikut, memcpy akan terjadi pada 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 ); Untuk menghindari salinan memori yang tidak berguna, disarankan untuk mengambil datagram dari cache Socket dengan makeDatagram(const size_t length) . Kemudian gunakan netudp::SharedDatagram ini untuk membuat serial data. Dan hubungi:
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); Jika Anda tidak puas dengan perilaku Socket , atau jika Anda ingin mengejek Socket tanpa ada ketergantungan ke QtNetwork . Dimungkinkan untuk memperluas ISocket untuk menggunakan fungsi dasarnya.
isRunning / isBounded .Anda perlu mengesampingkan:
bool start() : Mulai soket. Restart otomatis untuk bertahan hidup dari kesalahan diharapkan. Jangan lupa menelepon ISocket::start dari awal.bool stop() : Hentikan soket. Hapus semua tugas berjalan, cache kosong, buffer, dll ... jangan lupa menelepon ISocket::stop di awal. Untuk memastikan pembersihan maksimum, selalu hentikan setiap bahkan jika menghentikan bagian mana pun gagal.joinMulticastGroup(const QString& groupAddress) : Implementasi untuk bergabung dengan grup multicast. Jangan lupa menelepon ISocket::joinMulticastGroup .leaveMulticastGroup(const QString& groupAddress) : Implementasi untuk meninggalkan grup multicast. Jangan lupa menelepon 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 dan Worker terutama bekerja berpasangan, jadi jika mengesampingkan satu, sering masuk akal untuk mengesampingkan yang lain.
Alasan untuk mengganti Worker :
Datagram khusus Alasan untuk mengganti Socket
Worker khusus.Datagram khusus. Menggunakan Datagram khusus dapat mengurangi salinan memori tergantung pada aplikasi Anda.
Worker .Socket::sendDatagram(SharedDatagram, ...) dengan itu.Socket untuk menggunakannya saat menelepon dengan Socket::sendDatagram(const uint8_t*, ...) . memcpy akan terjadi. Jadi jangan gunakan Datagram khusus untuk tujuan itu. # 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; }
}; Saat mewarisi dari SocketWorker , Anda dapat mengganti:
bool isPacketValid(const uint8_t* buffer, const size_t length) const : dipanggil setiap kali datagram diterima. Periksa apakah suatu paket valid tergantung pada protokol Anda. Implementasi default Kembalikan true. Anda dapat menambahkan cek CRC atau semacamnya. Mengembalikan false di sini akan menambah penghitung rxInvalidPacketTotal di Socket .void onReceivedDatagram(const SharedDatagram& datagram) : dipanggil setiap kali datagram yang valid tiba. Implementasi Default memancarkan sinyal receivedDatagram . Menggantikan fungsi ini untuk menambahkan sistem pesan khusus, atau deserialisasi khusus.std::shared_ptr<Datagram> makeDatagram(const size_t length) : buat Datagram khusus untuk rx.Worker , hubungi void onSendDatagram(const SharedDatagram& datagram) untuk mengirim Datagram ke jaringan.SocketWorker mewarisi dari QObject , jadi gunakan Q_OBJECT Macro untuk menghasilkan sinyal khusus.Contoh:
# 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);
}
}Menyesuaikan pekerja sebagian besar masuk akal saat berjalan di utas terpisah. Kalau tidak, itu tidak akan memberikan peningkatan kinerja. Jangan lupa untuk menelepon
Socket::setUseWorkerThread(true).
Saat mewarisi dari Socket Anda dapat menggantikan:
bool std::unique_ptr<SocketWorker> createWorker() const : Buat pekerja kustom.void onDatagramReceived(const SharedDatagram& datagram) : menangani datagram di sana. Implementasi default memancarkan datagramReceived sinyalstd::shared_ptr<Datagram> makeDatagram(const size_t length) : Buat Datagram khusus yang akan digunakan dalam Socket::sendDatagram(const uint8_t*, ...) .Socket mewarisi dari QObject , jadi gunakan Q_OBJECT Macro untuk menghasilkan sinyal khusus.Contoh:
# 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);
}
} Contoh ini menunjukkan gema antara server dan klien. Soket Kirim paket ke klien, klien membalas paket yang sama. Ctrl+C untuk berhenti.
$ > 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
> ... Contoh ini juga pecah menjadi 2 contoh: NetUdp_EchoClient & NetUdp_EchoServer .
Tunjukkan cara bergabung dengan grup IP multicast. Kirim paket dan bacalah kembali melalui 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(); harus dipanggil di utama untuk mendaftarkan tipe QML. Contoh ini menunjukkan cara mengirim datagram unicast sebagai string ke 127.0.0.1:9999 . Jangan lupa untuk memulai soket sebelum mengirim pesan apa pun.
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 ( )
}
}Contoh ini menunjukkan cara menerima datagram. Jangan lupa untuk mulai mendengarkan alamat dan port. Datagram selalu diterima sebagai string. Dapat dengan mudah didekodekan untuk memanipulasi array byte.
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 )
}Kirim Datagram Multicast bekerja hampir sama dengan Unicast. Satu -satunya perbedaan adalah bahwa Anda mengontrol antarmuka yang digunakan data.
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 ( )
}Kemudian kirim data seperti di unicast:
socket . sendDatagram ( {
address : "239.1.2.3" ,
port : 9999 ,
data : "My Data"
} )Untuk menerimanya, berlangganan ke grup multicast dan pilih antarmuka mana.
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 )
}Perpustakaan ini juga menyediakan objek alat yang menunjukkan setiap fungsi QMLS. Ini dimaksudkan untuk debug cepat, atau fungsi uji jika UI belum dibangun.
Grafik dependensi dapat dihasilkan dengan:
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 Gunakan format otomatis untuk cpp , cmake . scripts folder berisi skrip helper. Disarankan untuk mengatur format otomatis di dalam IDE.
cd scripts
./clangformat.sh
./cmakeformat.sh
? Netudp -> netudp? netudp-> netudp ➖ hapus ketergantungan spdlog dalam rasa qcdebug/qcwarning ➕ kelola dependensi melalui CPM ♻️ Pekerja: antarmuka-> iface untuk menghindari konflik dengan msvc # tentukan ♻ mer WorkerPrivate ♻️ Pimpl Socketprivate ♻️ Pimpl RecycledDatagrivate? Jadikan recycler Privat Karena Semua Recycler Termasuk dipindahkan ke dalam PIMPPL ⚡️ netudp_enable_unity_build? Interfaceprovider: Gunakan steady_clock alih -alih sistem untuk menghindari rollback? Cetak Perintah Build Di CMake Update ReadMe dengan grafik dependensi
? Sertakan header QelapsedTimer yang hilang dalam pekerja
? Gunakan pointer mentah untuk utas pekerja & pekerja. ? ️ Ini harus memperbaiki masalah saat port tidak sepenuhnya dirilis.
NETUDP_ENABLE_PCH , NETUDP_ENABLE_EXAMPLES , NETUDP_ENABLE_TESTSresize .multicastOutgoingInterfaces alih -alih multicastInterfaceName . Jika multicastOutgoingInterfaces adalah paket kosong akan dikirim pada setiap antarmuka.multicastListenOnAllInterfaces dan buat default ketika multicastListeningInterfaces kosong.