NetUDPは、データグラムを送信および受信できるUDPソケットを提供します。
このライブラリはメンテナンスモードです。これは、重要なバグのみが修正されることを意味します。新しい機能は追加されません。このライブラリを引き継ぎたい場合は、そうしてください。
ライブラリには、いくつかの巨大なデザインの欠陥、フランカのAPI、テストがあります。それは私が若かったときの私の経験の一部であり、私はそれから多くを学びました。 QTを使用する場合、サードパーティライブラリのようなものに依存するのではなく、むしろQUdpSocket使用する必要があるため、APIを修正したくありません。
依存関係を可能な限り小さくしておくので、保守性がより簡単になります。
箱から出している2つの主要なクラスは、 SocketとRecycledDatagramです。サーバーを作成するだけで、開始します。次に、データグラムを送信して受信します。サーバーはマルチキャストグループに参加して、マルチキャストパケットを受け取ることができます。
Socket 、別々のスレッドまたはメインスレッドで実行できるWorkerを使用します。
すべてのデータグラム割り当てはstd::shared_ptr<Datagram>に保存されます。これにより、データグラムオブジェクト構造は、何も再割り当てずに既に割り当てられていることを再利用できます。
ISocket 、機能なしでSocketを表すように継承できます。
SocketとWorkerを継承して、サーバーとワーカー間のカスタム通信を実装できます。たとえば、ワーカースレッドでシリアル化/脱必要になる可能性のあるカスタムオブジェクトを送信します。
必要に応じてカスタムデータコンテナを使用すると、 Datagramを継承できます。たとえば、データが既に構造でシリアル化されている場合。 Datagram内のその構造への参照を置くと、 RecycledDatagramへのコピーは避けてください。
基本的なクライアント/ソケットはexamples/EchoClientServer.cppにあります。
この例は、ポート9999で127.0.0.1にアドレスを与えるためにデータグラムを送信するサーバーを作成する方法を示しています。
# 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)を呼び出すことで明示的に指定できます。ソケットがDatagramを受信した場合(つまり、
inputEnabledtrueであり、setRxPortを呼び出す)、RXポートが使用されます。このデフォルトの動作を変更するにはsetSeparateRxTxSockets(true)を呼び出します。
この例は、ポート9999のアドレス127.0.0.1でパケットを受信する方法を示しています。
# 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 :マルチキャストパケット用の発信インターフェイス。指定されていない場合、パケットはすべてのインターフェイスにデフォルトでプラグアンドプレイエクスペリエンスを提供します。内部的には、 Socket複数の情報を追跡して、何が起こっているのかを考えています。
isBounded 、ソケットが現在ネットワークインターフェイスにバインドされているかどうかを示します。*xBytesPerSeconds最後の瞬間に受信/送信されたすべてのバイトの平均値です。この値は毎秒更新されます。 * can be replaced by t and r*xBytesTotal合計は、開始後から受信/送信バイトを受け取ります。 * can be replaced by t and r*xPacketsPerSeconds最後の瞬間に受信/送信されたすべてのパケットの平均値です。この値は毎秒更新されます。 * can be replaced by t and r*xPacketsTotal合計は、開始後から受信/送信パケットを受け取ります。 * 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 );役に立たないメモリコピーを避けるためにmakeDatagram(const size_t length)を使用してSocketキャッシュからデータグラムを取得することをお勧めします。次に、この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動作に満足していない場合、またはQtNetworkに依存せずにSocketを模倣する場合。 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::sendDatagram(const uint8_t*, ...)で呼び出すときに使用するSocketをカスタマイズします。 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 :データグラムを受信するたびに呼び出されます。プロトコルに応じてパケットが有効かどうかを確認してください。デフォルトの実装はtrueを返すだけです。 CRCチェックなどを追加できます。ここでfalseを返すと、 SocketのrxInvalidPacketTotalカウンターが増加します。void onReceivedDatagram(const SharedDatagram& datagram) :有効なデータグラムが到着するたびに呼び出されます。デフォルトの実装は、 receivedDatagram信号を排出します。この関数をオーバーライドして、カスタムメッセージングシステム、またはカスタムデラシリアル化を追加します。std::shared_ptr<Datagram> makeDatagram(const size_t length) :rxのカスタムDatagramを作成します。Workerでカスタムメッセージシステムを介してカスタムシリアル化を実装する場合は、 void onSendDatagram(const SharedDatagram& datagram)を呼び出して、データグラムをネットワークに送信します。SocketWorker QObjectから継承することを忘れないでください。Q_Object 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) :そこにデータグラムを処理します。デフォルトの実装は、 datagramReceived信号を発しますstd::shared_ptr<Datagram> makeDatagram(const size_t length) : Socket::sendDatagram(const uint8_t*, ...)で使用されるカスタムDatagramを作成します。QObjectからSocketが継承されることを忘れないでください。Q_Object Macroを使用して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);
}
}この例は、サーバーとクライアントの間のエコーを示しています。ソケットはクライアントにパケットを送信し、クライアントは同じパケットに返信します。 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 2つの例にも掲載されています。
マルチキャストIPグループに参加する方法を示します。パケットを送信して、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(); QMLタイプを登録するには、メインで呼び出される必要があります。この例は、 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 )
}ユニキャストとほぼ同じマルチキャストデータグラムの作業を送信します。唯一の違いは、データがどのインターフェイスに進んでいるかを制御することです。
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機能を実証するツールオブジェクトも提供します。これは、UIがまだ構築されていない場合、クイックデバッグ、または機能をテストすることを目的としています。
依存関係グラフは、次のとおりに生成できます。
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➖qcdebug/qcwarningのフレーバーのspdlog依存関係を取り外しますcpm経由の依存関係を管理する依存関係♻♻状:interface-> iface#msvc#iface#define interface struct https://stackoverflow.com/questions/25234203/ wits-is-is-interface-wordewword-msvchmsvchms pimpl socketprivateすべてのリサイクラーが含まれているため、 recyclerプライベートにします。 InterfaceProvider:システムの代わりにSteady_Clockを使用して、ロールバックを避けますか? cmake update readmeの依存関係グラフでビルドコマンドを印刷する
?労働者にQelapsedtimerヘッダーがありません
?労働者と労働者のスレッドには生のポインターを使用します。 ?§これは、ポートが完全にリリースされていない場合に問題を修正する必要があります。
NETUDP_ENABLE_PCH 、 NETUDP_ENABLE_EXAMPLES 、 NETUDP_ENABLE_TESTSresizeメソッドを使用してデータグラムのサイズを変更します。multicastInterfaceNameの代わりにmulticastOutgoingInterfacesを紹介します。 multicastOutgoingInterfacesが空の場合、すべてのインターフェイスで送信されます。multicastListenOnAllInterfacesを削除し、 multicastListeningInterfacesが空の場合にデフォルトにします。