Skip to content

Commit

Permalink
Merge pull request #238 from naivisoftware/mod_napudp_improvements
Browse files Browse the repository at this point in the history
Multiple mod_napudp improvements
  • Loading branch information
cklosters committed Sep 28, 2022
2 parents 751c469 + 06ef67b commit cf1a45c
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 77 deletions.
27 changes: 25 additions & 2 deletions modules/napudp/src/udpadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,34 @@ namespace nap
if(!errorState.check(mThread !=nullptr, "Thread cannot be nullptr"))
return false;

mThread->registerAdapter(this);
return true;
}


void UDPAdapter::onDestroy()
{
mThread->removeAdapter(this);
}


bool UDPAdapter::start(utility::ErrorState& errorState)
{
if(!onStart(errorState))
return false;

mThread->registerAdapter(this);

return true;
}


void UDPAdapter::stop()
{
onStop();

mThread->removeAdapter(this);
};


bool UDPAdapter::handleAsioError(const std::error_code& errorCode, utility::ErrorState& errorState, bool& success)
{
if(errorCode)
Expand All @@ -61,4 +78,10 @@ namespace nap

return false;
}


asio::io_context& UDPAdapter::getIOContext()
{
return mThread->getIOContext();
}
}
52 changes: 42 additions & 10 deletions modules/napudp/src/udpadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

// Nap includes
#include <nap/device.h>
#include <nap/resource.h>
#include <nap/resourceptr.h>

Expand All @@ -21,11 +22,11 @@ namespace nap
* process() is automatically called by the thread this adapter links to.
* Both UDPClient & UDPServer extend UDPAdapter.
*/
class NAPAPI UDPAdapter : public Resource
class NAPAPI UDPAdapter : public Device
{
friend class UDPThread;

RTTI_ENABLE(Resource)
RTTI_ENABLE(Device)
public:
/**
* Constructor
Expand All @@ -37,28 +38,59 @@ namespace nap
*/
virtual ~UDPAdapter();

/**
* Initialization
* @param error contains error information
* @return true on success
*/
virtual bool init(utility::ErrorState& errorState) override;
/**
* Initialization
* @param error contains error information
* @return true on success
*/
virtual bool init(utility::ErrorState& errorState) override final;

/**
* called on destruction
*/
virtual void onDestroy() override;
virtual void onDestroy() override final;

/**
* Start the adapter. Called after initialization.
* When called it is safe to assume that all dependencies have been resolved up to this point.
* Internally calls virtual method 'onStart' that is implemented in derived class
* Upon successfull start, registers adapter to UDP thread
* @param errorState The error state
* @return: true on success
*/
virtual bool start(utility::ErrorState& errorState) override final;

/**
* Called when the adapter needs to be stopped, but only if start has previously been called on this Device.
* It is safe to assume that when stop is called the device is in a 'started' state. Called in reverse init order.
* Removed adapter from UDP thread
*/
virtual void stop() override final;

// Properties
ResourcePtr<UDPThread> mThread; ///< Property: 'Thread' the udp thread the adapter registers itself to
bool mAllowFailure = false; ///< Property: 'AllowFailure' if binding to socket is allowed to fail on initialization

protected:
/**
* Called by start method and needs to be implemented by derived class
* @param errorState The error state
* @return: true on success
*/
virtual bool onStart(utility::ErrorState& errorState) = 0;

/**
* Called by stop method and needs to be implemented by derived class
*/
virtual void onStop() = 0;

/**
* called by a UDPThread
*/
virtual void process() = 0;

bool handleAsioError(const std::error_code& errorCode, utility::ErrorState& errorState, bool& success);
};

asio::io_context& getIOContext();
};
}
28 changes: 12 additions & 16 deletions modules/napudp/src/udpclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ namespace nap
class UDPClient::Impl
{
public:
Impl(asio::io_context& context) : mIOContext(context){}

// ASIO
asio::io_context mIOService;
asio::io_context& mIOContext;
asio::ip::udp::endpoint mRemoteEndpoint;
asio::ip::udp::socket mSocket{ mIOService };
asio::ip::udp::socket mSocket{ mIOContext };
};

//////////////////////////////////////////////////////////////////////////
Expand All @@ -52,22 +54,22 @@ namespace nap
{}


bool UDPClient::init(utility::ErrorState& errorState)
bool UDPClient::onStart(utility::ErrorState& errorState)
{
// create asio implementation
mAsio = std::make_unique<UDPClient::Impl>();
mImpl = std::make_unique<UDPClient::Impl>(getIOContext());

// when asio error occurs, init_success indicates whether initialization should fail or succeed
bool init_success = false;

// try to open socket
asio::error_code asio_error_code;
mAsio->mSocket.open(udp::v4(), asio_error_code);
mImpl->mSocket.open(udp::v4(), asio_error_code);
if(handleAsioError(asio_error_code, errorState, init_success))
return init_success;

// enable/disable broadcast
mAsio->mSocket.set_option(asio::socket_base::broadcast(mBroadcast), asio_error_code);
mImpl->mSocket.set_option(asio::socket_base::broadcast(mBroadcast), asio_error_code);
if(handleAsioError(asio_error_code, errorState, init_success))
return init_success;

Expand All @@ -76,22 +78,16 @@ namespace nap
if(handleAsioError(asio_error_code, errorState, init_success))
return init_success;

mAsio->mRemoteEndpoint = udp::endpoint(address, mPort);

// init UDPAdapter, registering the client to an UDPThread
if (!UDPAdapter::init(errorState))
return false;
mImpl->mRemoteEndpoint = udp::endpoint(address, mPort);

return true;
}


void UDPClient::onDestroy()
void UDPClient::onStop()
{
UDPAdapter::onDestroy();

asio::error_code err;
mAsio->mSocket.close(err);
mImpl->mSocket.close(err);
if (err)
{
nap::Logger::error(*this, "error closing socket : %s", err.message().c_str());
Expand Down Expand Up @@ -146,7 +142,7 @@ namespace nap
while(mQueue.try_dequeue(packet_to_send))
{
asio::error_code err;
mAsio->mSocket.send_to(asio::buffer(&packet_to_send.data()[0], packet_to_send.size()), mAsio->mRemoteEndpoint, 0, err);
mImpl->mSocket.send_to(asio::buffer(&packet_to_send.data()[0], packet_to_send.size()), mImpl->mRemoteEndpoint, 0, err);

if(err)
{
Expand Down
28 changes: 14 additions & 14 deletions modules/napudp/src/udpclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,6 @@ namespace nap
*/
virtual ~UDPClient();

/**
* Initializes the UDP client
* @param error contains error information
* @return true on success
*/
bool init(utility::ErrorState& errorState) override;

/**
* called on destruction
*/
void onDestroy() override;

/**
* Makes a copy of the packet and queues if for sending.
* Call send(std::move(packet)) if you want to move the packet instead.
Expand All @@ -70,15 +58,27 @@ namespace nap
bool mBroadcast = false; ///< Property: 'Broadcast' set option to broadcast

protected:
/**
* Starts the UDP client and creates the socket
* @param error contains error information
* @return true on success
*/
bool onStart(utility::ErrorState& errorState) override final;

/**
* Called when socket needs to be closed
*/
void onStop() override final;

/**
* The process function
*/
void process() override;
void process() override final;

private:
// Client specific ASIO implementation
class Impl;
std::unique_ptr<Impl> mAsio;
std::unique_ptr<Impl> mImpl;
std::vector<nap::uint8> mBuffer;

// Threading
Expand Down
39 changes: 27 additions & 12 deletions modules/napudp/src/udpserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
#include <thread>

RTTI_BEGIN_CLASS(nap::UDPServer)
RTTI_PROPERTY("Port", &nap::UDPServer::mPort, nap::rtti::EPropertyMetaData::Default)
RTTI_PROPERTY("IP Address", &nap::UDPServer::mIPAddress, nap::rtti::EPropertyMetaData::Default)
RTTI_PROPERTY("Port", &nap::UDPServer::mPort, nap::rtti::EPropertyMetaData::Default)
RTTI_PROPERTY("IP Address", &nap::UDPServer::mIPAddress, nap::rtti::EPropertyMetaData::Default)
RTTI_PROPERTY("Multicast Groups", &nap::UDPServer::mMulticastGroups, nap::rtti::EPropertyMetaData::Default)
RTTI_END_CLASS

using namespace asio::ip;
Expand All @@ -37,10 +38,12 @@ namespace nap
class UDPServer::Impl
{
public:
Impl(asio::io_context& service) : mIOContext(service){}

// ASIO
asio::io_context mIOService;
asio::io_context& mIOContext;
asio::ip::udp::endpoint mRemoteEndpoint;
asio::ip::udp::socket mSocket{ mIOService };
asio::ip::udp::socket mSocket{ mIOContext };
};

//////////////////////////////////////////////////////////////////////////
Expand All @@ -55,17 +58,17 @@ namespace nap
{}


bool UDPServer::init(utility::ErrorState& errorState)
bool UDPServer::onStart(utility::ErrorState& errorState)
{
// create asio implementation
mASIO = std::make_unique<UDPServer::Impl>();
mImpl = std::make_unique<UDPServer::Impl>(getIOContext());

// when asio error occurs, init_success indicates whether initialization should fail or succeed
bool init_success = false;

// try to open socket
asio::error_code asio_error_code;
mASIO->mSocket.open(udp::v4(), asio_error_code);
mImpl->mSocket.open(udp::v4(), asio_error_code);
if(handleAsioError(asio_error_code, errorState, init_success))
return init_success;

Expand All @@ -85,10 +88,22 @@ namespace nap

// try to bind socket
nap::Logger::info(*this, "Listening at port %i", mPort);
mASIO->mSocket.bind(udp::endpoint(address, mPort), asio_error_code);
mImpl->mSocket.bind(udp::endpoint(address, mPort), asio_error_code);
if(handleAsioError(asio_error_code, errorState, init_success))
return init_success;

// join multicast groups
for(const auto& multicast_group : mMulticastGroups)
{
auto multicast_address = make_address(multicast_group, asio_error_code);
if (handleAsioError(asio_error_code, errorState, init_success))
return init_success;

mImpl->mSocket.set_option(multicast::join_group(multicast_address), asio_error_code);
if (handleAsioError(asio_error_code, errorState, init_success))
return init_success;
}

// init UDPAdapter, registering the server to an UDPThread
if (!UDPAdapter::init(errorState))
return false;
Expand All @@ -97,12 +112,12 @@ namespace nap
}


void UDPServer::onDestroy()
void UDPServer::onStop()
{
UDPAdapter::onDestroy();

asio::error_code asio_error_code;
mASIO->mSocket.close(asio_error_code);
mImpl->mSocket.close(asio_error_code);

if(asio_error_code)
{
Expand All @@ -114,13 +129,13 @@ namespace nap
void UDPServer::process()
{
asio::error_code asio_error;
size_t available_bytes = mASIO->mSocket.available(asio_error);
size_t available_bytes = mImpl->mSocket.available(asio_error);
if(available_bytes > 0)
{
// fill buffer
std::vector<uint8> buffer;
buffer.resize(available_bytes);
mASIO->mSocket.receive(asio::buffer(buffer), 0, asio_error);
mImpl->mSocket.receive(asio::buffer(buffer), 0, asio_error);

if (!asio_error)
{
Expand Down
Loading

0 comments on commit cf1a45c

Please sign in to comment.