Issue with DatagramSocket


#1

I have a JUCE client/server application where the server periodically broadcasts a udp 'hello' message on a defined port.

When a client starts, it listens on that port for the 'hello' message, and if it is correct, passes the ip address to the main client who connects on a different port and all is well.

I have this working using the boost libraries (on windows) but have had so much trouble building boost for osx that I an now using the StreamingSocket class for the comms, but although the DatagramSocket socket receives the 'hello' message, it is not possible (as far as I can see) to obtain the ip address of the sender if the message.

Could this be added to the Datagram class please?

Below is the boost version of the detector thread class.

 


#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <boost/thread.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/interprocess/creation_tags.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <string>
#include <queue>
#include "packetdefs.h"
#include "DeskDetector.h"
#include "IpComms.h"
#define BUFFER_SIZE 4096
using namespace std;

using    boost::asio::ip::udp;
using    boost::asio::ip::tcp;

CDeskDetector::CDeskDetector(void) : Thread ("DeskDetector")
{
    DBG ("DeskDetector ctor");
}

CDeskDetector::~CDeskDetector(void)
{
    DBG ("DeskDetector dtor");
}
void CDeskDetector::run ()
{
    DBG ("DetectorThread ");
    boost::asio::io_service io_service;
    udp::socket*    socket = new udp::socket (io_service);
    boost::asio::ip::address ipAddr = boost::asio::ip::address_v4::any();
    boost::asio::ip::udp::endpoint listen_endpoint (ipAddr, GENESYS_ADVERT_PORT);
    boost::asio::ip::udp::endpoint m_sender_endpoint;
    socket->open(listen_endpoint.protocol());
    socket->bind(listen_endpoint);
    boost::array<char, 32> buf; 
    string    callme = ADVERT_MESSAGE;
    bool    detected = false;
    while (!threadShouldExit ()) {
        buf.fill (0);
        try {
            socket->receive_from (boost::asio::buffer(buf), m_sender_endpoint);
            string s = buf.data ();
            if (s.compare (ADVERT_MESSAGE) != 0)
                continue;
            //    Server detected.    Tell the parent (client) the ip address to connect to
            //                        then tell it to connect and exit. (Job done!).
            pParent->m_ServerAddress = m_sender_endpoint.address ();
            if (pParent->ConnectToServer ()) {
                detected = true;
                break;
            }
        }
        catch (std::exception& e)  {
            string err = e.what ();
            DBG ((char *) err.c_str());
            break;
        }
    }
    socket->close ();
    delete socket;
    if (detected)    {
        DBG ("CDeskDetector found server\n");
        pParent->setDetecting (false);
    }
}

#2

It's a good request but I don't have time to do the research right now - if you could get me started by suggesting some code for both Posix and win32 that gets the IP address of the socket then I'd be happy to review/refactor it and add it to the class.


#3

I think that the key is that DatagramSockets read from the same helper SocketHelpers::readSocket, which calls 'recv'. 

This is fine in streaming sockets, because the connection is 1:1 and integrity, etc. happens at a lower level, but doesn't always work for UDP applications. Take a simple UDP discovery mechnism (close to what is seemingly described above). I open a datagram socket and listen for broadcasts. When someone looks for me, they broadcast a discovery packet and I respond with a connection, etc.

In lots of protocols, the connection information is included in the discovery packet: 'hey, connect to me at this address/socket!' But not all protocols do this, so you sometimes need to know the address information of the broadcaster. This is done with recvfrom, which you pass in an address structure to which get's populated with each received packet.

Since this is very much a Datagram thing, I would probably add a DatagramSocket::readFrom, which returns address information (and uses recvfrom instead of recv).

If you want to get the address information associated with a particular socket, that is another issue. I think you long ago factored in my code to get all the underlying IP4 addresses for adapters. Retrieving the bound information for a given socket is similar.

Hope that helps.