NetUdp는 데이터 그램을 보내고받을 수있는 UDP 소켓을 제공합니다.
이 라이브러리는 유지 보수 모드에 있습니다. 그것은 중요한 버그 만 고정 될 것임을 의미합니다. 새로운 기능이 추가되지 않습니다. 이 라이브러리를 인수하려면 그렇게하십시오.
라이브러리에는 큰 디자인 결함, 플랑크인 API 및 테스트가 있습니다. 어렸을 때의 경험의 일부 였고, 나는 그것으로부터 많은 것을 배웠습니다. QT를 사용할 때 타사 라이브러리에 의존해서는 안되며 QUdpSocket 사용하기 때문에 API를 수정하고 싶지 않습니다.
의존성을 최대한 작게 유지하므로 유지 가능성이 더 쉽습니다.
상자에서 작동하는 두 가지 주요 클래스는 Socket 과 RecycledDatagram 입니다. 단순히 서버를 만들고 시작하십시오. 그런 다음 데이터 그램을 보내고받습니다. 서버는 멀티 캐스트 그룹에 가입하여 멀티 캐스트 패킷을 수신 할 수 있습니다.
Socket 별도의 스레드 또는 주 스레드에서 실행할 수있는 Worker 사용합니다.
모든 데이터 그램 할당은 std::shared_ptr<Datagram> 에 저장됩니다. 이를 통해 데이터 그램 객체 구조를 재사용 할 수 있습니다.
기능이없는 Socket 나타 내기 위해 ISocket 상속받을 수 있습니다.
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)호출하여 명시 적으로 지정할 수 있습니다.소켓에도 데이터 그램을받는 경우 (예 :
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) 신호를 통해 오류를 관찰 할 수 있습니다. 소켓이 바인딩되지 않거나 어떤 일이 발생하면 작업자는 Watchdog 타이머를 시작하여 소켓을 다시 시작합니다.
기본 재시작 시간은 5 초로 설정되지만 watchdogPeriod 속성을 통해 변경할 수 있습니다. 속성은 밀리 초로 표현됩니다.
기본적으로 내부 소켓이 포트와의 인터페이스에 제한된 경우 Worker 들어오는 데이터 그램을 받게됩니다. Socket 내부에 해당 데이터 그램을받지 않으려면 setInputEnabled(false) 호출하십시오.
multicastGroups 듣는 멀티 캐스트 주소 목록입니다. MultiCast Group에 가입하려면 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 매크로를 사용하여 사용자 정의 신호를 생성하십시오.예:
# 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 만듭니다.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);
}
} 이 예제는 서버와 클라이언트 사이의 에코를 보여줍니다. 소켓은 패킷을 클라이언트에게 보내고 클라이언트는 동일한 패킷에 응답합니다. 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 그룹에 가입하는 방법을 보여줍니다. 패킷을 보내고 루프백을 통해 다시 읽으십시오.
$ > 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 ➕ cpm을 통한 종속성 관리 : interface-> msvc # //stackoverflow.com/questions/25234203/what-is- interface-ky-ky-in-msvc와의 충돌을 피하기 위해 QCDEBUG/QCWARNING의 풍미에서 SPDLOG 종속성을 제거하십시오. WorkerPrivate ate️ pimpl socketprivate ♻️ pimpl Recycleddatagramprivate? 모든 재활용 업체가 포함되어 있기 때문에 recycler 비공개로 만드십시오. pimpl ⚡️ netudp_enable_unity_build 내부로 이동 했습니까? Interfaceprovider : 롤백을 피하기 위해 시스템 대신 steady_clock을 사용합니까? CMAKE 업데이트의 인쇄 명령은 종속성 그래프로 readme입니다.
? 작업자에 누락 된 QelapsedTimer 헤더를 포함하십시오
? 작업자 및 작업자 스레드에 원시 포인터를 사용하십시오. ? 닐 포트가 완전히 릴리스되지 않았을 때 문제를 해결해야합니다.
NETUDP_ENABLE_PCH , NETUDP_ENABLE_EXAMPLES , NETUDP_ENABLE_TESTSresize 방법으로 데이터 그램을 크기를 조정할 수 있습니다.multicastInterfaceName multicastOutgoingInterfaces 소개하십시오. multicastOutgoingInterfaces 인터페이스 인 경우 빈 패킷이 모든 인터페이스에서 전송됩니다.multicastListenOnAllInterfaces 제거하고 multicastListeningInterfaces 비어있을 때 기본값으로 만드십시오.