boost asio receive on linux
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 2 of 2

Thread: boost asio receive on linux

  1. #1
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,145

    boost asio receive on linux

    Consider the following example for a udp asynchronous echo server as given in the boost documentation:

    Code:
    //
    // async_udp_echo_server.cpp
    // ~~~~~~~~~~~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
    //
    // Distributed under the Boost Software License, Version 1.0. (See accompanying
    // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    //
    
    #include <cstdlib>
    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::udp;
    
    class server
    {
    public:
      server(boost::asio::io_service& io_service, short port)
        : io_service_(io_service),
          socket_(io_service, udp::endpoint(udp::v4(), port))
      {
        socket_.async_receive_from(
            boost::asio::buffer(data_, max_length), sender_endpoint_,
            boost::bind(&server::handle_receive_from, this,
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));
      }
    
      void handle_receive_from(const boost::system::error_code& error,
          size_t bytes_recvd)
      {
        if (!error && bytes_recvd > 0)
        {
          socket_.async_send_to(
              boost::asio::buffer(data_, bytes_recvd), sender_endpoint_,
              boost::bind(&server::handle_send_to, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }
        else
        {
          socket_.async_receive_from(
              boost::asio::buffer(data_, max_length), sender_endpoint_,
              boost::bind(&server::handle_receive_from, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }
      }
    
      void handle_send_to(const boost::system::error_code& /*error*/,
          size_t /*bytes_sent*/)
      {
        socket_.async_receive_from(
            boost::asio::buffer(data_, max_length), sender_endpoint_,
            boost::bind(&server::handle_receive_from, this,
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));
      }
    
    private:
      boost::asio::io_service& io_service_;
      udp::socket socket_;
      udp::endpoint sender_endpoint_;
      enum { max_length = 1024 };
      char data_[max_length];
    };
    
    int main(int argc, char* argv[])
    {
      try
      {
        if (argc != 2)
        {
          std::cerr << "Usage: async_udp_echo_server <port>\n";
          return 1;
        }
    
        boost::asio::io_service io_service;
    
        using namespace std; // For atoi.
        server s(io_service, atoi(argv[1]));
    
        io_service.run();
      }
      catch (std::exception& e)
      {
        std::cerr << "Exception: " << e.what() << "\n";
      }
    
      return 0;
    }
    The above example compiles and runs as expected under both Windows and Linux targets. With the use of udp::v4() the socket binds to any local interface (I'm guessing the first valid address on the first available NIC). But what if you want it to bind to a particular local ip address? Based on the documentation that would mean changing the above code to something along the lines of:

    Code:
    //
    // async_udp_echo_server.cpp
    // ~~~~~~~~~~~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2010 Christopher M. Kohlhoff (chris at kohlhoff dot com)
    //
    // Distributed under the Boost Software License, Version 1.0. (See accompanying
    // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    // #include <cstdlib>
    #include <iostream>
    #include <boost/bind.hpp>
    #include <boost/asio.hpp>
    
    using boost::asio::ip::udp;
    
    class server
    {
    public:
      server(boost::asio::io_service& io_service, const std::string& local_address, short port)
        : io_service_(io_service),
          socket_(io_service, udp::endpoint(boost::asio::ip::address::from_string(local_address), port))
      {
        socket_.async_receive_from(
            boost::asio::buffer(data_, max_length), sender_endpoint_,
            boost::bind(&server::handle_receive_from, this,
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));
      }
    
      void handle_receive_from(const boost::system::error_code& error,
          size_t bytes_recvd)
      {
        if (!error && bytes_recvd > 0)
        {
          socket_.async_send_to(
              boost::asio::buffer(data_, bytes_recvd), sender_endpoint_,
              boost::bind(&server::handle_send_to, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }
        else
        {
          socket_.async_receive_from(
              boost::asio::buffer(data_, max_length), sender_endpoint_,
              boost::bind(&server::handle_receive_from, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
        }
      }
    
      void handle_send_to(const boost::system::error_code& /*error*/,
          size_t /*bytes_sent*/)
      {
        socket_.async_receive_from(
            boost::asio::buffer(data_, max_length), sender_endpoint_,
            boost::bind(&server::handle_receive_from, this,
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred));
      }
    
    private:
      boost::asio::io_service& io_service_;
      udp::socket socket_;
      udp::endpoint sender_endpoint_;
      enum { max_length = 1024 };
      char data_[max_length];
    };
    
    int main(int argc, char* argv[])
    {
      try
      {
        if (argc != 3)
        {
          std::cerr << "Usage: async_udp_echo_server <local_ip_address> <port>\n";
          return 1;
        }
    
        boost::asio::io_service io_service;
    
        std::string local_address(argv[1]);
    
        using namespace std; // For atoi.
        server s(io_service, local_address, atoi(argv[2]));
    
        io_service.run();
      }
      catch (std::exception& e)
      {
        std::cerr << "Exception: " << e.what() << "\n";
      }
    
      return 0;
    }
    The above compiles under both Windows and Linux targets, however, the code only works as expected under Windows. Under Linux, the async_receive_from doesn't ever call handle_receive_from, suggesting that data is not getting through the network stack. I've tried various linux platforms (Centos 5, Montavista 5 and Ubuntu 9.10), and they all behave the same way. One of my colleagues wrote a bit of code using straight sockets instead of asio, and that code seems to have a similar problem. If any of you have experienced a similar problem under Linux with either boost asio or the standard sockets api and know what the problem is, then please can you let me know - I've been messing with this for the better part of two days.
    Last edited by PredicateNormative; October 26th, 2010 at 10:25 AM.

  2. #2
    Join Date
    May 2007
    Location
    Scotland
    Posts
    1,145

    Re: boost asio receive on linux

    Having noticed that a lot of people have read this post, I thought I would share my findings, just in case any of you hit a similar problem in the future.

    I didn't mention this before because I didn't realise its significance, but, the packets that I have been trying to receive are broadcast packets. The issue is, that Windows and Linux have differing mindsets when it comes to accepting broadcast packets. If you bind a socket to a specific protocol, port and address under Windows, then you will receive packets specifically for that protocol/port/address triplet plus any broadcast packets on that port and subnet for that address. However, under Linux, if you bind a socket to a specific protocol, port and address you will only receive packets specifically for that protocol/port/address triplet, and will not receive broadcast packets. If you want to receive broadcast packets under Linux, then you have to bind a socket to the any address option.

    I must admit, although I am a fan of Linux, I feel that Windows has the better behaviour with respect to broadcast packets. In my application I want to bind to two specific protocol/port/address triplets, plus I want them to receive broadcast packets. I find myself slightly aggrevated that I now have to write additional code that requires an extra socket and logic for filtering on broadcast packets and figuring if they apply to either or neither of the protocol/port/address triplets that I have set up on the other two sockets. This extra code isn't required for a Windows platform.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center