I am a beginner of multicast programming. I am using boost::asio to scribe some multicast data.
I wrote a program with the code
boost::array<char,1500> _receiveBuf;
void WaitForNextRead()
{
_receiveSocket->async_receive_from(
boost::asio::buffer(_receiveBuf, 1500),
_receiveEndPoint,
boost::bind(
&AsyncReadHandler,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void AsyncReadHandler(
const boost::system::error_code& error, // Result of operation.
std::size_t bytes_transferred // Number of bytes received.
)
{
std::cout << _receiveEndPoint.address() << ":" << _receiveEndPoint.port() << ":" << std::string(_receiveBuf.c_array(), bytes_transferred) << "\n";
WaitForNextRead();
}
int main()
{
std::string address;
int port;
std::cin >> address;
std::cin >> port;
boost::asio::io_service ioService;
_receiveSocket = new udp::socket( ioService );
_receiveSocket->open( udp::v4() );
_receiveSocket->set_option( udp::socket::reuse_address(true) );
_receiveSocket->bind( udp::endpoint( address::from_string("0.0.0.0"), port ) );
_receiveSocket->set_option( multicast::join_group( address::from_string(address) ) );
_receiveEndPoint.address(address::from_string(address));
_receiveEndPoint.port(port);
WaitForNextRead();
ioService.run();
return 0;
}
My instance A is joining: 239.1.1.1:12345
My instance B is joining: 239.1.127.1:12345
It is very weird that both instance A and B will get the message from both address!!
Did I miss out some socket option?
PS:
Here is my routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
224.0.0.0 * 240.0.0.0 U 0 0 0 eth1
I think I found the answer.
Refer to:
http://man7.org/linux/man-pages/man7/ip.7.html
https://bugzilla.redhat.com/show_bug.cgi?id=231899
Linux has a bug that the broadcast IP_ADD_MEMBERSHIP is a global action to all sockets even when part of another process. We need to set the option IP_MULTICAST_ALL to zero (0) to fix this problem.
Related
I have one udp server receiving messages from multiple remote clients. When it receives one message, I copy the endpoint and reply to the client at the same IP address on port 5000 where each client is listening.
I have tried multiple debbuging strategies, and printing the endpoint right before I send the reply message gives me the correct IP address and port.
The sender:
std::cout << udp_remote_endpoint.address().to_string();
std::string str(packet.begin(), packet.end());
std::cout << str << std::endl;
io_service.post(
[this, packet]()
{
udp_socket.async_send_to(
boost::asio::buffer(packet),
udp_remote_endpoint,
boost::bind(
&uds::handle_write,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
);
On the receiver, I get the udp_remote_endpoint and before sending, I set the socket endpoint:
new_addr.endpoint = socket.get_udp_remote_endpoint();
new_addr.endpoint.port(5000);
socket.set_udp_remote_endpoint(new_addr.endpoint);
For example, this output:
192.168.1.131K-131-1559147491761155
Is actually sending to the IP 192.168.1.130. The message contents are correct "K-131-1559147491761155"
Solved!
I removed the io_service.post and it worked!
`std::cout << udp_remote_endpoint.address().to_string();
std::string str(packet.begin(), packet.end());
std::cout << str << std::endl;
//io_service.post(
// [this, packet]()
// {
udp_socket.async_send_to(
boost::asio::buffer(packet),
udp_remote_endpoint,
boost::bind(
&uds::handle_write,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
// }
//);`
I am attempting to use boost::asio to implement a simple device discovery protocol. Basically I want to send a broadcast message (port 9000) with 2 byte payload. Then read the response from the device (assuming currently it exists). In wireshark I can see the broadcast is been sent and that the device is responding. However, in my example code I get that the bytes returned is 0 in the UDP read, not 30 bytes of data.
No. Time Source Destination Protocol Length
1 0.00000 192.168.0.20 255.255.255.255 UDP 44 52271 -> 9000 Len = 2
2 0.00200 192.168.0.21 192.168.0.20 UDP 72 9000 -> 52271 Len = 30
Should I be reading from a different endpoint than broadcastEndpoint? How do I get the end point?
I am new to asio and trying to teach my self, but I cannot work what I have done wrong.
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
class udp_find {
public:
udp_find(boost::asio::io_context& service, unsigned int port)
: broadcastEndpoint_(boost::asio::ip::address_v4::broadcast(), port),
socket_(service)
{
socket_.open(boost::asio::ip::udp::v4());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.set_option(boost::asio::socket_base::broadcast(true));
boost::array<unsigned int, 2> data = {255, 255};
socket_.async_send_to(
boost::asio::buffer(data, 2), broadcastEndpoint_,
boost::bind(&udp_find::handle_send, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_receive(const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "Received Data" << bytes_transferred << std::endl;
}
void handle_send(const boost::system::error_code& error, std::size_t bytes_transferred)
{
std::cout << "Sent Data" << bytes_transferred << std::endl;
socket_.async_receive_from(
boost::asio::buffer(buffer_), broadcastEndpoint_,
boost::bind(&udp_find::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
boost::asio::ip::udp::socket socket_;
boost::array<char, 128> buffer_;
boost::asio::ip::udp::endpoint broadcastEndpoint_;
};
int main()
{
boost::asio::io_context service;
udp_find(service, 9000);
service.run();
}
Your first problem is Udefined Behaviour.
You start asynchronous operations on a temporary object of type udp_find. The object is destructed immediately after construction, so it doesn't exist anymore even before you start any of the async work (service.run()).
That is easily fixed by making udp_find a local variable instead of a temporary:
udp_find op(service, 9000);
Now sending works for me. You will want to test that receiving works as well. In my netstat output it appears that the UDP socket is bound to an ephemeral port. Sending a datagram to that port makes the test succeed for me.
You might want to actually bind/connect to the broadcast address before receiving (the endpoint& parameter to async_receive_from is not for that, I think it is an output parameter).
I'm trying to use boost::asio for my new little hobby project but I'm having trouble getting the server to read the right data. Sending it works fine, I've checked with wireshark and the bytes [0 0 0 4] followed by [5 0 0 0] are sent. But on the server side I receive [16 -19 105 0] which makes me rather confused.
Here's how I send it, working perfectly when viewed through wireshark:
boost::asio::io_service io;
tcp::resolver resolver(io);
tcp::resolver::query query("localhost", boost::lexical_cast<string>("40001"));
tcp::resolver::iterator endpoints = resolver.resolve(query);
tcp::socket socket(io);
boost::asio::connect(socket, endpoints);
header h(5);
header::storage data = h.store();
boost::asio::write(socket, boost::asio::buffer(&data[0], header::header_size()));
This is a stripped down version of my server class. handle_read_header is called with the correct number of bytes, but headerbuffer contains weird values, [16 -19 105 0].
class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
public:
tcp_connection(boost::asio::io_service& io)
: _socket(io)
{
memset(&headerbuffer[0], 0, headerbuffer.size());
}
void start() {
_socket.async_read_some(boost::asio::buffer(&headerbuffer[0], header::header_size()), boost::bind(&tcp_connection::handle_read_header, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read_header(const boost::system::error_code& error, std::size_t numBytes) {
if(!error) {
BOOST_LOG_SEV(logger, loglvl::debug) <<"handle_read_header, " <<numBytes <<"/" <<header::header_size() <<" bytes";
if (numBytes == header::header_size()) {
std::stringstream ss;
for(u32 a = 0; a < numBytes; ++a) {
ss <<(int)headerbuffer[a] <<" ";
}
BOOST_LOG_SEV(logger, loglvl::debug) <<"header data: " <<ss.str();
mCurrentHeader.load(headerbuffer);
mRemaining = mCurrentHeader.size();
BOOST_LOG_SEV(logger, loglvl::debug) <<"got header with size " <<mCurrentHeader.size();
}
} else {
BOOST_LOG_SEV(logger, loglvl::debug) <<"error " <<error;
}
}
private:
header mCurrentHeader;
std::array<char, 128> headerbuffer;
boost::asio::ip::tcp::socket _socket;
};
(Almost) complete code can be found at http://paste2.org/U97HHaH3
It turned out to be a sneaky, but at the same time obvious, error. tcp_connection::ptr is a shared_ptr. In tcp_server.h I call start() on it, but then nothing referes to it, which makes the shared_ptr, reasonably, assume it can be deleted. So the function is running, but the member variables have been cleared.
No idea why it worked some of the times, but I assume that's in the land of undefined behaviour.
I apologize in advance if the question has been previously answered, but I've searched and found nothing that helps me. As indicated by the question's title, I'm trying to broadcast a package from a server to a set of clients listening for any message.
The client will count the number of messages it receives during one second.
The server side of things goes like this:
class Server
{
public:
Server(boost::asio::io_service& io)
: socket(io, udp::endpoint(udp::v4(), 8888))
, broadcastEndpoint(address_v4::broadcast(), 8888)
, tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error))
, timer(io, boost::posix_time::milliseconds(20))
{
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
timer.async_wait(tickHandler);
}
private:
void Tick(const boost::system::error_code&)
{
socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint);
timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20));
timer.async_wait(tickHandler);
}
private:
udp::socket socket;
udp::endpoint broadcastEndpoint;
boost::function<void(const boost::system::error_code&)> tickHandler;
boost::asio::deadline_timer timer;
boost::array<char, 100> buffer;
};
It is initialized and run in the following way:
int main()
{
try
{
boost::asio::io_service io;
Server server(io);
io.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
return 0;
}
This (apparently) works fine. Now comes the client...
void HandleReceive(const boost::system::error_code&, std::size_t bytes)
{
std::cout << "Got " << bytes << " bytes\n";
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " <host>\n";
return 1;
}
try
{
boost::asio::io_service io;
udp::resolver resolver(io);
udp::resolver::query query(udp::v4(), argv[1], "1666");
udp::endpoint serverEndpoint = *resolver.resolve(query);
//std::cout << serverEndpoint.address() << "\n";
udp::socket socket(io);
socket.open(udp::v4());
socket.bind(serverEndpoint);
udp::endpoint senderEndpoint;
boost::array<char, 300> buffer;
auto counter = 0;
auto start = std::chrono::system_clock::now();
while (true)
{
socket.receive_from(boost::asio::buffer(buffer), senderEndpoint);
++counter;
auto current = std::chrono::system_clock::now();
if (current - start >= std::chrono::seconds(1))
{
std::cout << counter << "\n";
counter = 0;
start = current;
}
}
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
This works when running both the server and client on the same machine, but doesn't when I run the server on a machine different from that of where I run the client.
First thing is, it seems odd to me that I have to resolve the server's address. Perhaps I don't know how broadcasting really works, but I thought the server would send a message using its socket with the broadcast option turned on, and it would arrive to all the sockets in the same network.
I read you should bind the client's socket to the address_v4::any() address. I did, it doesn't work (says something about a socket already using the address/port).
Thanks in advance.
PS: I'm under Windows 8.
I am a bit surprised this works on the same machine. I would not have expected the client, listening to port 1666, to receive data being sent to the broadcast address on port 8888.
bind() assigns a local endpoint (composed of a local address and port) to the socket. When a socket binds to an endpoint, it specifies that the socket will only receive data sent to the bound address and port. It is often advised to bind to address_v4::any(), as this will use all available interfaces for listening. In the case of a system with multiple interfaces (possible multiple NIC cards), binding to a specific interface address will result in the socket only listening to data received from the specified interface[1]. Thus, one might find themselves obtaining an address through resolve() when the application wants to bind to a specific network interface and wants to support resolving it by providing the IP directly (127.0.0.1) or a name (localhost).
It is important to note that when binding to a socket, the endpoint is composed of both an address and port. This is the source of my surprise that it works on the same machine. If the server is writing to broadcast:8888, a socket bound to port 1666 should not receive the datagram. Nevertheless, here is a visual of the endpoints and networking:
.--------.
.--------.|
.--------. address: any address: any .--------.||
| | port: any / \ port: 8888 | |||
| server |-( ----------->| address: broadcast |----------> )-| client ||'
| | \ port: 8888 / | |'
'--------' '--------'
The server binds to any address and any port, enables the broadcast option, and sends data to the remote endpoint (broadcast:8888). Clients bound to the any address on port 8888 should receive the data.
A simple example is as follows.
The server:
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Server binds to any address and any port.
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 0));
socket.set_option(boost::asio::socket_base::broadcast(true));
// Broadcast will go to port 8888.
ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888);
// Broadcast data.
boost::array<char, 4> buffer;
socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint);
}
The client:
#include <iostream>
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Client binds to any address on port 8888 (the same port on which
// broadcast data is sent from server).
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 8888 ));
ip::udp::endpoint sender_endpoint;
// Receive data.
boost::array<char, 4> buffer;
std::size_t bytes_transferred =
socket.receive_from(boost::asio::buffer(buffer), sender_endpoint);
std::cout << "got " << bytes_transferred << " bytes." << std::endl;
}
When the client is not co-located with the server, then it could be a variety of network related issues:
Verify connectivity between the server and client.
Verify firewall exceptions.
Verify broadcast support/exceptions on the routing device.
Use a network analyzer tool, such as Wireshark, to verify that the time to live field in the packets is high enough that it will not be discarded during routing.
1. On Linux, broadcast datagrams received by an adapter will not be passed to a socket bound to a specific interface, as the datagram's destination is set to the broadcast address. On the other hand, Windows will pass broadcast datagrams received by an adapter to sockets bound to a specific interface.
See code. :P
I am able to receive new connections before async_accept() has been called. My delegate function is also never called so I can't manage any connections I receive, rendering the new connections useless. ;)
So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?
Thanks!
AlexSocket::AlexSocket(boost::asio::io_service& s): myService(s)
{
//none at the moment
connected = false;
listening = false;
using boost::asio::ip::tcp;
mySocket = new tcp::socket(myService);
}
AlexSocket::~AlexSocket()
{
delete mySocket;
}
bool AlexSocket::StartListening(int port)
{
bool didStart = false;
if (!this->listening)
{
//try to listen
acceptor = new tcp::acceptor(this->myService);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
acceptor->open(endpoint.protocol());
acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor->bind(endpoint);
//CAN GET NEW CONNECTIONS HERE (before async_accept is called)
acceptor->listen();
didStart = true; //probably change?
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
//acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
}
else //already started!
return false;
this->listening = didStart;
return didStart;
}
//this function is never called :(
void AlexSocket::NewConnection(tcp::socket* s, const boost::system::error_code& error)
{
cout << "New Connection Made" << endl;
//Start new accept async
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
}
bool AlexSocket::ConnectToServer(std::string toConnectTo, string port)
{
if (connected)
return false;
this->serverConnectedTo = toConnectTo;
this->serverPort = port;
ip::tcp::resolver resolver(myService);
ip::tcp::resolver::query newQuery(toConnectTo, port);
ip::tcp::resolver::iterator myIter = resolver.resolve(newQuery);
ip::tcp::resolver::iterator end;
//error
boost::system::error_code error = boost::asio::error::host_not_found;
//try each endpoint
bool connected = false;
while (error && myIter != end)
{
ip::tcp::endpoint endpoint = *myIter++;
std::cout << endpoint << std::endl;
mySocket->close();
mySocket->connect(*myIter, error);
if (error)
{
//try to connect, if it didn't work return false
cout << "Did not Connect" << endl << error << endl;
}
else
{
//was able to connect
cout << "Connected!" << endl;
connected = true;
}
myIter++;
}
this->connected = connected;
return connected;
}
EDIT:
I've changed my code to reflect what the answers so far have said. I am passing in an io_service to the ctor of my class. As you can see below, main is NOT calling run on the service, so I would assume that nothing should be able to connect right?
I have put my debugger on the listen() line and went to "canyouseeme.org". Typed in 57422 and hit Connect. Couldn't. Ran the listen() line. Was able to connect. This shouldn't be possible right? Like never? :(
No idea what to do anymore. main() is below.
int main()
{
boost::asio::io_service s;
AlexSocket test(s);
test.StartListening(57422);
test.ConnectToServer("localhost", "57422");
cout << "Enter something to quit" << endl;
int a2;
cin >> a2;
return 0;
}
So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?
Why do you think this is happening? If you posted the complete code, that would greatly help. When I take your snippet and put a boilerplate main and io_service::run() around it, everything works fine.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <iostream>
using namespace boost::asio;
class Socket {
public:
Socket(
io_service& io_service
) :
_io_service( io_service ),
_acceptor( new ip::tcp::acceptor(io_service) )
{
}
bool start(int port)
{
//try to listen
ip::tcp::endpoint endpoint(ip::tcp::v4(), port);
_acceptor->open(endpoint.protocol());
_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
_acceptor->bind(endpoint);
//CAN GET NEW CONNECTIONS HERE (before async_accept is called)
_acceptor->listen();
ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
_acceptor->async_accept(
*temp,
boost::bind(
&Socket::NewConnection,
this,
temp,
boost::asio::placeholders::error
)
);
}
void NewConnection(
ip::tcp::socket* s,
const boost::system::error_code& error
)
{
std::cout << "New Connection Made" << std::endl;
//Start new accept async
ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
_acceptor->async_accept(
*temp,
boost::bind(
&Socket::NewConnection,
this,
temp,
boost::asio::placeholders::error
)
);
}
private:
io_service& _io_service;
ip::tcp::acceptor* _acceptor;
};
int
main()
{
io_service foo;
Socket sock( foo );
sock.start(1234);
foo.run();
return 0;
}
compile and run:
macmini:~ samm$ g++ -lboost_system accept.cc
macmini:~ samm$ ./a.out
New Connection Made
telnet from another terminal
macmini:~ samm$ telnet 127.0.0.1 1234
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
I think you are mixing different things here.
On the one hand, you are creating a socket for data exchange. A socket is nothing more than an endpoint of an inter-process communication flow across a computer network. Your boost::asio::tcp::socket uses the TCP-protocoll for the communication; but in general, a socket can use other protocols. For opening a tcp-socket, one uses generally the sequence open-bind-listen-accept on the host.
On the other hand, you analyse the (underlying) TCP-connection.
So there are two different things here. While for the socket the connection is considered "established" only after the "accept" of the host, the underlying TCP-connection is already established after the client connects to a listening socket. (One the server side, that connection is put on a stack, from which it is dequeue when you call accept()).
So the only way to prohibit connection in your case, is not to call listen().
If you are truly getting a new connection at the point when you call acceptor->listen() then I am puzzled by that. What are you using to determine whether you've gotten a connection or not? The io_service is typically quite "reactive" in that it only reacts to events that it has been explicitly told to react to.
In your example above, the only thing I see that would cause a "new connection" to be initiated is calling async_accept. Additionally, what you described makes little sense from a low-level sockets standpoint (using BSD sockets, typically you must call bind, listen, and accept in that order, and only then can a new connection be made).
My suspicion is that you've actually got some faulty logic somewhere. Who calls StartListening and how often is it called (it should only need to be called once). You've gone through a bunch of extra effort to setup your acceptor object that's usually not necessary in Asio - you can typically just use the acceptor constructor to create an acceptor with all the parameters you need, and then just call async_accept:
acceptor = new tcp::acceptor(
this->myService,
boost::asio::ip::tcp::endpoint(
boost::asio::ip::tcp::v4(),
port),
true);
tcp::socket* tempNewSocket = new tcp::socket(this->myService);
acceptor->async_accept(
*tempNewSocket,
boost::bind(
&AlexSocket::NewConnection,
this,
tempNewSocket,
boost::asio::placeholders::error) );