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
)
);
// }
//);`
Related
I'm creating a server / client paradigm using UDP, but the Rust server is not receiving the C++ client messages. I have been able to successfully do Rust server / Rust client and C++ server / Rust client communication.
This leads me to believe that there is an issue with my C++ code, or there is some type of miscommunication when sending C++ buffers to Rust, but I have used code that I beleive works. This is only being sent from and to the same computer and has not been expanded to computer to computer.
I am no expert with UDP / TCP so I may be doing something incorrectly
Rust server:
use std::net::UdpSocket;
fn main() {
let udp: UdpSocket = UdpSocket::bind("0.0.0.0:12000")
.expect("Failed to bind to address for sending/receiving messages");
udp.connect("127.0.0.1:12683")
.expect("Failed to connect address receiving our messages");
//The below (recv_from) is set to blocking
let mut buf = [0; 20];
let (number_of_bytes, src_addr) = udp.recv_from(&mut buf).expect("Didn't receive data");
let filled_buf = &mut buf[..number_of_bytes];
println!("{:?}", filled_buf);
}
C++ client:
boost::asio::io_service io_service;
ip::udp::socket socket( io_service );
ip::udp::endpoint remote_endpoint;
std::cout << "sending reply..." << std::endl;
socket.open( ip::udp::v4() );
remote_endpoint = ip::udp::endpoint( ip::address::from_string( "127.0.0.1" ), 12000 );
unsigned char buff[8]{ 5,5,5,5,5,5,5,5 };
boost::system::error_code err;
//auto sent = socket.send_to( buffer( "Jane Doe"), remote_endpoint, 0, err );
auto sent = socket.send_to( buffer( buff ), remote_endpoint, 0, err );
std::cout << err << std::endl;
std::cout << "Sent: " << sent << std::endl;
socket.close();
The C++ client states that the data was sent (sent variable) and there is no err (err variable). However, my Rust server never receives the data. It is set to non-blocking so it just sits there waiting to receive data (its looking at port 12000 while the client is sending to port 12000).
When you connect a UDP socket, that causes the UDP socket to only receive datagrams from the address it is connected to. Servers should not connect their UDP sockets.
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 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.
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.
I want a Stateful communication but not like boost's echo server example. My socket will be ready for reading forever and whenever it receives a new data it will call a virtual method dataAvailable(string) however it can do async_write anytime.
void connected(const boost::system::error_code &ec) {
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
std::cout << ">> Session::connected()" << std::endl;
}
void handler_read(const boost::system::error_code &ec, size_t bytes_transferred) {
if(ec) {
std::cout << ec.message() << std::endl;
} else {
//std::copy(_buffer, _buffer+bytes_transferred, data.begin());
std::string data(_buffer, _buffer+bytes_transferred);
std::cout << ">> Session[ " << id() << "]" << "::handler_read(): " <<
bytes_transferred << " " << data << std::endl;
boost::asio::async_write(_socket, boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_write, this,
boost::asio::placeholders::error));
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this,
// boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
//call dataAvailable(_buffer);
}
}
void handler_write(const boost::system::error_code &ec) {
if(ec) {
std::cout << ec.message() << std::endl;
} else {
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this,
// boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
}
}
Is this Implementation Okay ? as multiple threads may do read and write operations. where in write operation is updation of some cells in matrix
Why it doesn't work (doesn't echo's the received string) when I use async_read instead of async_read_some
In my listening Server I am nowhere calling the listen method. but still its working. Then Why there is a listen method ? and when it is used ?
I want to get the notification when the client socket is exited from Client side. e.g. Client has closed connection. How can I do it ? I way comming out is by reading an End Of File in read_handler But is that the only Way ?
I've a Session class, and each session have one socket. I am storing Session* colection in a Session Manager. now when I close a socket and delete it that session becomes null. and it may happen in the middle of a vector. So How to safely remove that null session ?
There is one serious issue with your code. Imagine that you have asymmetric network link and are able to receive much faster than send.
Receive message
Do async_write (but it takes time)
Schedule next read
Receive next message
Do second async_write (the first one is not completed yet, you get garbage one other side of link)
Schedule next read
First async_write finishes and schedules one more async_read_some
To sum up, you request asio to do multiple reads and writes to the same socket at once.
Why it doesn't work (doesn't echo's the received string) when I use async_read instead of async_read_some
Are you sending enough data (max_length)? Maybe you would like to use boost::asio::transfer_at_least(min_length)?
In my listening Server I am nowhere calling the listen method. but still its working. Then Why there is a listen method ? and when it is used ?
You probably create acceptor object somewhere.
I want to get the notification when the client socket is exited from Client side. e.g. Client has closed connection. How can I do it ? I way comming out is by reading an End Of File in read_handler But is that the only Way ?
You could create wrapper, which will take EOF and do some cleanup. Then you wrap all your custom handlers with wrapper and pass wrapper as a handler to boost::asio. This way you have only one place to handle EOF error, if you prefer to.
I've a Session class, and each session have one socket. I am storing Session* colection in a Session Manager. now when I close a socket and delete it that session becomes null. and it may happen in the middle of a vector. So How to safely remove that null session ?
Use boost::shared_ptr in handlers and so on and boost::shared_ptr or boost::weak_ptr in Session Manager's vector.