Help with zeroconf and basic programming question

6 posts / 0 new
Last post
Owen
Offline
Last seen: 1 year 6 months ago
Joined: 24 Oct 2010 - 00:24
Help with zeroconf and basic programming question

Hi everyone,

I'm working on an app that uses zeroconf to discover a monome connected to my computer. I have it working... but my code is really ugly :( Basically, I wrote it in C to allow for it to work crossplatform (hopefully) and it uses static functions. I want to write the service name, port, and address to a value tree, but I can't access the local member variables in a static function, and making the ValueTree static doesn't allow me to initialize it.

What I ended up doing was making a data model that was a global singleton... but that seems real ugly, and makes it difficult to reuse the zerconf classes in new apps.

The zerconf code more or less looks like this, but inside a JUCE thread:

Resolving a service
http://read.pudn.com/downloads197/sourcecode/internet/927990/O'Reilly%20Zeroconf%20Book%20example%20code/C%20code/test4.c__.htm

Browsing for service
http://read.pudn.com/downloads197/sourcecode/internet/927990/O'Reilly%20Zeroconf%20Book%20example%20code/C%20code/test3.c__.htm

...and if anyone is interested, here is a good chapter from O-Rielly on implementing zeroconf. http://oreilly.com/catalog/bonjour/chapter/ch07.pdf

any ideas, or thoughts on good ways to hook this into a main component would be greatly appreciated.

Mike
Offline
Last seen: 3 weeks 4 days ago
Joined: 27 Jan 2011 - 22:34
Re: Help with zeroconf and basic programming question

Hi, and thanks for the code. Would you care to elaborate a bit on how to use it? I appreciate the reference to the book, which I will try to read, but I'm still unsure what code goes where, and how...

[Insert signature here]

Mike
Offline
Last seen: 3 weeks 4 days ago
Joined: 27 Jan 2011 - 22:34
Re: Help with zeroconf and basic programming question

I'm sure we can share ideas on how to implement it.

[Insert signature here]

StrangeMan
Offline
Last seen: 7 months 5 days ago
Joined: 21 Apr 2012 - 12:21
Re: Help with zeroconf and basic programming question

It appears, that I'm just writing my own monome apps with JUCE. The zeroconf stuff is already working great.

I basically have a class to manage all connected devices. This class internally stores a list of available devices. In addition I have two "Thread" classes. One, that will poll bonjour for changes (only one thread of that type exists), and - one object for each new device that is found - another thread class to poll bonjour for the resolve-callback specific to each device. Let me show you some code:

I start searching for monomers with this:

/// Stop old thread
	m_bonjour_thread->stopThread(10);
	// Delete all old bonjour resolve threads (they stopped running anyway, but there objects 
	// are still there)
    {
        // The list could in parallel be accessed by the bonjour thread, so we better use a lock here
        GenericScopedLock<SpinLock> a_lock(m_bonjour_resolve_threads_lock);
        m_bonjour_resolve_threads.clear();
    }
	
    {
        GenericScopedLock<CriticalSection> a_lock(m_critical_section_devicelist);
        // Clear device list.
        m_devices.clear();
    }

        DNSServiceErrorType err;
	err = DNSServiceBrowse( &m_bonjour_ref,
							0,
							0,
							"_monome-osc._udp",
							"",
							BonjourCallback, // this will be called by bonjour
							this ); // when entering the callback, bonjour will also pass this -> nice way to access some objects from within the callback
	if (err != 0)
		Log::logLine("Error: Error searching for monomes via Bonjour");
	else
	{
		Log::logLine("Started browsing for monomes via Bonjour");
		m_bonjour_thread->startThread(1);
	}

The Thread that is started in the last line is the first i mentioned above. It will run all the time and simply does this:

void BonjourThread::run()
{
	err = 0;

	while(!threadShouldExit())
	{
		err = DNSServiceProcessResult( *m_bonjour_ref );

		if( err != kDNSServiceErr_NoError )
			{
				Log::logLine("Error: Bonjour reports error code " + String(err));
				DNSServiceRefDeallocate( *m_bonjour_ref );
				*m_bonjour_ref = NULL;		
			}
        // suspend thread to check back later
        wait(50);
	}
}

So it actually only polls bonjour. When a new devices gets plugged in, or when an existing device is removed, then bonjour will answer by calling the Callback function mentioned in the code above. This function will remove the devices from an internal list (in case it was removed) or aks bonjour to resolve the connection details of this new device. This is done like this:

{...} // remove device from list, it unplugged... 

// if new device was found:
err =  DNSServiceResolve(&ref, 0, interfaceIndex, serviceName, regtype, replyDomain, BonjourResolveCallback, con);
				if (err == 0)
				{
					// Create Thread to wait for the callback
                                        {
                                              GenericScopedLock<SpinLock> a_lock(man->m_bonjour_resolve_threads_lock);
                                              man->m_bonjour_resolve_threads.push_back(std::tr1::shared_ptr<BonjourResolveThread>(new BonjourResolveThread(&ref)));
                                              man->m_bonjour_resolve_threads.back()->startThread(1);
					}
					Log::logLine("Resolving device connection details: " + String(serviceName));
				}

You see, that it spawns a new thread to resolve the connection details for the newly connected device. This resolve Thread is of the second type I mentioned above. It is added to a list and does this:
BonjourResolveThread::run()
{
	err = 0;

	err = DNSServiceProcessResult( *m_bonjour_ref );

	if( err != kDNSServiceErr_NoError )
		{
			R7Log::logLine("R7ViewportManager","Error: Bonjour resolve reports error code " + String(err));
			DNSServiceRefDeallocate( *m_bonjour_ref );
			*m_bonjour_ref = NULL;		
		}
	// Stop after work is done
}

Again, bonjour will call the resolve callback, when its ready. After I added the resolved device into my list, the resolve thread shuts down. It's object remains in the list, but the list will be cleared when the program shuts down or when the user requests to check for new devices (see the first lines of code, I posted). I use shared_ptr's, so when the list is cleared, the objects will be deleted, too.

So after all, it's not too complicated. You could also do all the resolve stuff for all devices inside one single thread. But AFAIK bonjour blocks your DNSServiceProcessResult calls until it is ready. Therefore I found it a better solution to create a new thread for each device.

Just be very careful with any of the lists: These can now be accessed from different threads at the same time - use locks! And there is another tricky thing: My callbacks always call other parts of my program (e.g. to update the gui, when a new device is plugged in). I use the listener-method for this. However, calling listeners from the threads is not a good idea, because you newer know, what your listeners do when you call them. They might call other parts of your software and you have no control, where your thread is working at in the end. One solution would be to use locks for everything. The better solution is to make the main thread call the listeners. I can explain how i did this, if you feel unsure about how to do that.

Please feel free to ask or share your ideas about my approach.

StrangeMan

PS: I'm a bloody beginner - so what i posted here might be buggy. It works for me right now but please share any thoughts on it, so i can rip out any potential problems.
PPS:Nice to see some monomers here, what exactly are you trying to write for the monome?

Mike
Offline
Last seen: 3 weeks 4 days ago
Joined: 27 Jan 2011 - 22:34
Re: Help with zeroconf and basic programming question

StrangeMan wrote:
It appears, that I'm just writing my own monome apps with JUCE.
What IS that?

[Insert signature here]

StrangeMan
Offline
Last seen: 7 months 5 days ago
Joined: 21 Apr 2012 - 12:21
Re: Help with zeroconf and basic programming question

Mike wrote:
What IS that?

See here: http://monome.org/devices