Search code examples
c++matlabboostudps-function

UDP Communication using Boost (for MATLAB s-function)


I'm trying to create an s-function (using C++ Boost library) for UDP communication.

Implementing the sender was fairly straightforward, 15 min job. I'm struggling to get the receiver to work.

I created the following in Visual Studio:

#define _WIN32_WINNT 0x0501
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING 

#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include <stdio.h>

typedef unsigned char   UINT8;
typedef unsigned short  UINT16;

using boost::asio::ip::udp;
using namespace std;

std::vector<char>       receive_buffer;

void process_received_frame(const boost::system::error_code& error, size_t received_frame_size) {
    if (error) {
        cout << "Receive failed: " << error.message() << "\n";        
        return;
    }

    size_t ByteCount = 0;

    std::cout << endl << "Received byte stream (Handler) [" << received_frame_size << "]: ";
    for (std::vector<char>::const_iterator iter = receive_buffer.cbegin(); iter != receive_buffer.cend(); iter++)
    {
        ByteCount++;

        printf("%02X ", (UINT8)*iter);

        if (ByteCount == received_frame_size)
        {
            break;
        }
    }
    std::cout << endl;
}

int main(int argc, char *argv[])
{
    boost::asio::io_service io_service;
    udp::socket             socket(io_service);   
    udp::endpoint           remote_endpoint = udp::endpoint(boost::asio::ip::address_v4::from_string("127.0.0.1"), 19001);

    socket.open(udp::v4());
    socket.bind(udp::endpoint(remote_endpoint));   

    receive_buffer.resize(255);

    try
    {
        socket.async_receive_from(boost::asio::buffer(receive_buffer),
            remote_endpoint,
            boost::bind(&process_received_frame, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
    }
    catch (const std::exception& exp)
    {
        printf("%s\n", exp.what());
    }

    //io_service.poll();
    io_service.run();

    cout << "End";

    std::cin.ignore();
}

I tried sending UDP to localhost:19001 from Simulink and was able to receive the UDP packets in Visual Studio. The handler (process_received_frame) gets called and everything seems to work, as expected.

But, given that, io_service::run() works in blocking mode, it pauses execution if there is nothing received on port 19001. So I tried using io_service::poll() (commented in the code above) instead. However, when I use poll(), it does not execute the handler. If I try to display the contents of 'receive_buffer' from main(), I get all 0s. Interestingly, when I single-step through the code for accessing the elements of 'receive_buffer' I do get the right values.

Not sure what is it that I'm doing wrong. Quite likely to be a school-boy-error.

When I convert this to an s-function for MATLAB-Simulink, it does the same thing - all zeros.

Any help would be much appreciated.

Cheers,


Solution

  • In your handler function, you need to call socket.async_receive_from at the end after processing the answer. io_service.run() returns when no more handler are in its processing queue.

    See the example from boost doc here: udp sync server example

    EDIT

    Rereading your question/comment, I'm not sure what your expected output or behavior is.

    If you're only expecting a single UDP frame, then maybe call io_service.run_one().

    If you don't want run() to block your main thread, you need to launch another thread to call run(). Something like:

    boost::asio::io_service io_service;
    // Process handlers in a background thread.
    boost::thread t(boost::bind(&io_service::run, &io_service));  
    ...
    

    io_service::run() is always a blocking call. Completion handlers can only be called from threads currently calling run(). The only time run() is going to return is when there is no more handlers in the queue (you stopped calling async_receive) or if you explicitly cancel the run() command by calling stop() or reset()