NetworkServiceDiscovery::AvailableServiceList

Hello everybody, I am trying to build an app that can communicate with Kyma via OSC. Kyma changes its IPAddress every time is turned on. usually connection with other OSC programs is made via Bonjour/Zeroconf.
my question is, its service is advertised as
‘_osc._udp’
is that a valid serviceType UID for NetworkServiceDiscovery::AvailableServiceList ?
how should I use that ? I know its sending and receiving port as they are fixed.

thanks.
d

No I don’t believe so. Bonjour isn’t too difficult to use, on macOS I think it’s just a matter of pulling in a header, maybe linking a framework (probably the same for iOS). On Windows there’s a library you have to link in too. Android I think has built-in support too so probably just a header to bring in, but I don’t really know because I’ve never really built for Android.

When I used bonjour some years ago I wrapped it in some classes making it more JUCE friendly, it wasn’t too hard. Unfortunately I don’t have access to that code anymore.

Thank Anthony, so your recommendation is to use Bonjour “inside” Juce?
I will have to study it :slight_smile:

I went back to remind myself about bonjour.

The key resources are this book: https://learning.oreilly.com/library/view/zero-configuration-networking/0596101007/ (chapters 6 and 7 are the most useful if you want to jump straight in)
and this https://developer.apple.com/bonjour/ (the linked video is particularly helpful from what I remember)

This is a quick command line app I wrote with JUCE that runs for 10 seconds printing any services it finds. It checks every second if there are any results to process, if there are and a service has been added or removed the callback function serviceAddedOrRemoved will be called.

Unfortunately I couldn’t find a way to use anything in JUCE to avoid using low level sockets. Ideally something like waitForReadiness in juce_Socket.cpp would be used but it isn’t exposed.

#include <JuceHeader.h>
#include <dns_sd.h>

//==============================================================================
class DNSServiceProcessor : private juce::Thread
{
public:
    DNSServiceProcessor (DNSServiceRef serviceRef)
        : juce::Thread {"DNSServiceProcessor"}
        , service {serviceRef}
    {
        startThread();
    }

    ~DNSServiceProcessor()
    {
        stopThread (1500);
    }

private:
    void run() override
    {
        struct timeval tv;
        tv.tv_sec = 1;
        tv.tv_usec = 0;

        const int dns_sd_fd {DNSServiceRefSockFD (service)};
        fd_set readfds;

        while ( ! threadShouldExit())
        {
            FD_ZERO (&readfds);
            FD_SET (dns_sd_fd, &readfds);

            if (select (dns_sd_fd + 1, &readfds, nullptr, nullptr, &tv) > 0)
            {
                if (FD_ISSET (dns_sd_fd, &readfds))
                    DNSServiceProcessResult (service);
            }
        }
    }

    DNSServiceRef service;
};

//==============================================================================
void serviceAddedOrRemoved (DNSServiceRef sdRef,
                            DNSServiceFlags flags,
                            uint32_t interfaceIndex,
                            DNSServiceErrorType errorCode,
                            const char* serviceName,
                            const char* regtype,
                            const char* replyDomain,
                            void* context)
{
    DBG (((flags & kDNSServiceFlagsAdd) ? "Added: " : "Removed: ") << serviceName);
}

//==============================================================================
int main (int argc, char* argv[])
{
    DNSServiceRef service;
    if (DNSServiceBrowse (&service, 0, 0, "_osc._udp", nullptr, &serviceAddedOrRemoved, nullptr) == kDNSServiceErr_NoError)
    {
        DNSServiceProcessor serviceProcessor {service};
        Thread::sleep (10000); // run the program for 10 seconds
    }
    return 0;
}

You can test this by running this the following in another terminal…

timeout 2s dns-sd -R "OSC Test" _osc._udp local 9904

This will create a service called "_osc._udp" in the local domain on port 9904 and then 2 seconds later it quits the process which removes the service.

4 Likes

thanks a lot Anthony, this is really helpful!

@Lucretio following this conversation I decided to write a JUCE module wrapper for Bonjour. You can access it here if you’re interested https://github.com/Anthony-Nicholls/jucey_bonjour

WARNING: I’ve currently only tested this on macOS, for example on Windows it will need the bonjour SDK. I have added a list of work still be done here https://github.com/Anthony-Nicholls/jucey_bonjour/issues.

That being said you can register, discover (browse), and resolve services. Including adding and reading TXT Records which are referred to as properties of a service in the module.

The only example of usage at the moment is the unit tests, you can open up the tests/tests.jucer and point the juce_core module to a local copy of the JUCE repo if you want to build it. All the tests are in https://github.com/Anthony-Nicholls/jucey_bonjour/blob/master/jucey_bonjour/bonjour/jucey_BonjourServiceTests.cpp

If you decide to use it let me know how you get on with it.

7 Likes

thanks Anthony, it looks like a lot of good work in here!
I will test it today.
in the meanwhile I was doing something really simple (essentially in plain C) to discover the name of my device with DNSServiceBrowse and to use the name in a DNSServiceGetAddrInfo to discover the dynamics IPAddress of my hardware.