Reputation: 23
I am learning multicast programming with socket.h and boost::asio. I am reviewing this link here, and they offer the following code using socket.h to implement a multicast server.
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
struct in_addr localInterface;
struct sockaddr_in groupSock;
int sd;
char databuf[1024] = "Multicast test message lol!";
int datalen = sizeof(databuf);
int main (int argc, char *argv[ ])
{
/* Create a datagram socket on which to send. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if(sd < 0)
{
perror("Opening datagram socket error");
exit(1);
}
else
printf("Opening the datagram socket...OK.\n");
/* Initialize the group sockaddr structure with a */
/* group address of 225.1.1.1 and port 5555. */
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr("226.1.1.1");
groupSock.sin_port = htons(4321);
/* Disable loopback so you do not receive your own datagrams.
{
char loopch = 0;
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)) < 0)
{
perror("Setting IP_MULTICAST_LOOP error");
close(sd);
exit(1);
}
else
printf("Disabling the loopback...OK.\n");
}
*/
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
localInterface.s_addr = inet_addr("203.106.93.94");
if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)) < 0)
{
perror("Setting local interface error");
exit(1);
}
else
printf("Setting the local interface...OK\n");
/* Send a message to the multicast group specified by the*/
/* groupSock sockaddr structure. */
/*int datalen = 1024;*/
if(sendto(sd, databuf, datalen, 0, (struct sockaddr*)&groupSock, sizeof(groupSock)) < 0)
{perror("Sending datagram message error");}
else
printf("Sending datagram message...OK\n");
/* Try the re-read from the socket if the loopback is not disable
if(read(sd, databuf, datalen) < 0)
{
perror("Reading datagram message error\n");
close(sd);
exit(1);
}
else
{
printf("Reading datagram message from client...OK\n");
printf("The message is: %s\n", databuf);
}
*/
return 0;
}
I am also reviewing an example of how to implement a multicast server using boost::asio here, and they present the following code.
//
// sender.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 <iostream>
#include <sstream>
#include <string>
#include <boost/asio.hpp>
#include "boost/bind.hpp"
#include "boost/date_time/posix_time/posix_time_types.hpp"
const short multicast_port = 30001;
const int max_message_count = 10;
class sender
{
public:
sender(boost::asio::io_service& io_service,
const boost::asio::ip::address& multicast_address)
: endpoint_(multicast_address, multicast_port),
socket_(io_service, endpoint_.protocol()),
timer_(io_service),
message_count_(0)
{
std::ostringstream os;
os << "Message " << message_count_++;
message_ = os.str();
socket_.async_send_to(
boost::asio::buffer(message_), endpoint_,
boost::bind(&sender::handle_send_to, this,
boost::asio::placeholders::error));
}
void handle_send_to(const boost::system::error_code& error)
{
if (!error && message_count_ < max_message_count)
{
timer_.expires_from_now(boost::posix_time::seconds(1));
timer_.async_wait(
boost::bind(&sender::handle_timeout, this,
boost::asio::placeholders::error));
}
}
void handle_timeout(const boost::system::error_code& error)
{
if (!error)
{
std::ostringstream os;
os << "Message " << message_count_++;
message_ = os.str();
socket_.async_send_to(
boost::asio::buffer(message_), endpoint_,
boost::bind(&sender::handle_send_to, this,
boost::asio::placeholders::error));
}
}
private:
boost::asio::ip::udp::endpoint endpoint_;
boost::asio::ip::udp::socket socket_;
boost::asio::deadline_timer timer_;
int message_count_;
std::string message_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: sender <multicast_address>\n";
std::cerr << " For IPv4, try:\n";
std::cerr << " sender 239.255.0.1\n";
std::cerr << " For IPv6, try:\n";
std::cerr << " sender ff31::8000:1234\n";
return 1;
}
boost::asio::io_service io_service;
sender s(io_service, boost::asio::ip::address::from_string(argv[1]));
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
I noticed the example using socket.h defines both a local interface and multicast addresses. However, the example using boost::asio only defines a multicast address. I will not include the code for the sake of brevity, but I noticed the code to implement a multicast receiver with both socket.h and boost::asio define both local interface and multicast addresses. But why do I not need to define a local interface address using boost::asio to implement a multicast server? Also, is boost::asio or socket.h faster if I want to send and receive multicast messages every few milliseconds?
Upvotes: 2
Views: 1539
Reputation: 51891
When using a multicast, one only needs to set the IP_MULTICAST_IF
option when datagrams should egress a specific interface. Boost.Asio provides this option with ip::multicast::outbound_interface
. When this option is not used, multicast transmissions are sent from the default interface, and the kernel may perform routing and forwarding through other interfaces. For instance, consider the case where a server has two NIC cards, connecting it to a LAN and a WAN. If the WAN is the default interface and multicast datagrams are to be sent to the LAN, then for a given socket, one could use the socket option to specify the outbound interface as the LAN.
Often times, the sender rarely cares about the exact endpoint (address and port) to which the socket binds. In both sender examples, the sender creates a socket, and defers to the kernel to bind to an endpoint. In the first example, the multicast messages sent from the local socket will egress the interface that has been assigned 203.106.93.94 address.
On the other hand, the receiver often cares about binding to a specific port. The receiver will bind the local socket to any appropriate address or defer to the kernel, and bind to the port matching the multicast endpoint's port. Once bound, a receiver will then have the socket join the multicast group, at which point the socket can begin receiving multicast datagrams. Note that for a given system, if multiple applications are interested in receiving the multicast datagram, then one should use the reuse_address
socket option.
Using the Boost.Asio examples as a reference, if one launches the sender with ./sender 239.255.0.1
and multiple receivers with ./receiver 0.0.0.0 239.255.0.1
, then the following sockets and binds occur:
.----------.
.----------.|
.--------. address: any address: any .----------.||
| | port: any / \ port: 30001 | |||
| sender |-( ----------->| address: 239.255.0.1 |----------> )-| receiver ||'
| | \ port: 30001 / | |'
'--------' '----------'
24000
.30001
and joins the 239.255.0.1
multicast group.239.255.0.1:30001
.239.255.0.1:30001
. The receiver's receive_from()
operation's sender_endpoint
argument will be populated with the sender's endpoint address and port 24000
.As far as performance goes, profiling the application would provide a definitive answer. The examples provided in the question are very different (synchronous vs. asynchronous), so directly comparing the two to determine which is faster may not be appropriate. In general, Boost.Asio will provide some overhead due to its abstractions. However, I have yet to work on an application where Boost.Asio's overhead was the problem, and its abstractions have saved me countless development and maintenance man-hours.
Upvotes: 1