How can I add an async timer to a boost UDP server? - c++

I got this code online and have been trying to add a timer to it so that it reads a packet every so often. I can't seem to figure out how to pass a callback function to the boost::async_wait command because I am getting this error:
server1.cpp: In member function ‘void UDPClient::handle_receive(const boost::system::error_code&, size_t)’:
server1.cpp:51:66: error: invalid use of non-static member function ‘void UDPClient::time_to_receive(const boost::system::error_code&)’
boost::asio::placeholders::error));
^
server1.cpp:33:6: note: declared here
void UDPClient::time_to_receive(const boost::system::error_code& error)
^~~~~~~~~
UDPClient Class:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
class UDPClient
{
public:
boost::asio::io_service io_service;
udp::socket socket;
udp::endpoint receiver_endpoint;
boost::asio::deadline_timer timer;
boost::array<char, 1024> recv_buffer;
UDPClient();
void time_to_receive(const boost::system::error_code& error);
void do_receive();
void handle_receive(const boost::system::error_code& error, size_t);
};
UDPClient::UDPClient()
: io_service(),
socket(io_service, {udp::v4(), 3643}),
timer(io_service, boost::posix_time::seconds(2))
{
do_receive();
io_service.run();
}
void UDPClient::time_to_receive(const boost::system::error_code& error)
{
do_receive();
}
void UDPClient::do_receive()
{
socket.async_receive_from(boost::asio::buffer(recv_buffer), receiver_endpoint,
boost::bind(&UDPClient::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void UDPClient::handle_receive(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "Received: '" << std::string(recv_buffer.begin(), recv_buffer.begin()+bytes_transferred) << "'\n";
timer.async_wait(boost::bind(time_to_receive,
boost::asio::placeholders::error));
}
int main()
{
UDPClient updclient;
}
One question I'm trying to answer with this code is if I spam the server from a client with a bunch of UDP packets will the server ignore all the packets during the async_wait?
Also, my main goal is to put this code into a quadcopter code I have. Will it work the way it is written to instantiate this class and have it read packets from a ground station to get user input?

The way you use bind with member function is wrong. Use it like shown below:
timer.async_wait(boost::bind(&UDPClient::time_to_receive, this,
boost::asio::placeholders::error));
As to why it is like that, I would recommend you to read the boost docs for that.
Also, I have modified the code to make it actually run like a server without exiting. For that I made following 2 changes:
Initialzation of io_service in main function and pass its reference to the class.
Initialize a io_service_work object. This acts as a perennial source of work to the io_service. Thus, io_service never returns from the run function unless the work object is destroyed.
The complete source:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
using boost::asio::ip::udp;
class UDPClient
{
public:
boost::asio::io_service& io_service;
udp::socket socket;
udp::endpoint receiver_endpoint;
boost::asio::deadline_timer timer;
boost::array<char, 1024> recv_buffer;
UDPClient(boost::asio::io_service&);
void time_to_receive(const boost::system::error_code& error);
void do_receive();
void handle_receive(const boost::system::error_code& error, size_t);
};
UDPClient::UDPClient(boost::asio::io_service& ios)
: io_service(ios),
socket(io_service, {udp::v4(), 3643}),
timer(io_service, boost::posix_time::seconds(2))
{
do_receive();
}
void UDPClient::time_to_receive(const boost::system::error_code& error)
{
do_receive();
}
void UDPClient::do_receive()
{
socket.async_receive_from(boost::asio::buffer(recv_buffer), receiver_endpoint,
boost::bind(&UDPClient::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void UDPClient::handle_receive(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "Received: '" << std::string(recv_buffer.begin(), recv_buffer.begin()+bytes_transferred) << "'\n";
timer.expires_from_now(boost::posix_time::seconds(2));
timer.async_wait(boost::bind(&UDPClient::time_to_receive, this,
boost::asio::placeholders::error));
}
int main()
{
boost::asio::io_service ios;
boost::asio::io_service::work wrk(ios);
UDPClient updclient(ios);
ios.run();
}
NOTE: Even though it is a server, the class is name Client. I am ignoring that :)

Related

boost asio acceptor.accept invalid argument

i am trying to code with boost asio in socket programming.
after setting boost in my ubuntu eclipse environment, i tried the sample code in boost web site.
but an error occur on acceptor.accept() function with invalid argument, like below
how can i fix this error ?
Invalid arguments '
Candidates are:
void accept(boost::asio::basic_socket<#10000,#10001> &, std::enable_if<&0[std::is_convertible<boost::asio::ip::tcp,#10000>::value],void>::type *)
boost::system::error_code accept(boost::asio::basic_socket<#10000,#10001> &, boost::system::error_code &, std::enable_if<&0[std::is_convertible<boost::asio::ip::tcp,#10000>::value],void>::type *)
void accept(boost::asio::basic_socket<boost::asio::ip::tcp,#10000> &, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &)
boost::system::error_code accept(boost::asio::basic_socket<boost::asio::ip::tcp,#10000> &, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &, boost::system::error_code &)
' boostTest.cpp /boostTest line 41 Semantic Error
and this is the code i am trying
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
return ctime(&now);
}
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
std::string message = make_daytime_string();
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
i am one who questioned about invalid argument error.
it seems that i have found a remedy, i still don't understand why though.
when i fixed argument of accept function, the error disappear.
the error seemed to tell that the function needs additional argument std::enable_if or boost::asio::ip::basic_endpoint
so i just add boost::asio::ip::tcp::endpoint like below
after that, it worked
tcp::endpoint endpoint = tcp::endpoint(tcp::v4(), port);
a.accept(sock, endpoint);

cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>

boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>
it does not tell where is this error is occurring :|, here is my codes
AsyncConnection
#ifndef _ASYNCCONNECTION_H_
#define _ASYNCCONNECTION_H_
#include <boost\shared_ptr.hpp>
#include <boost\enable_shared_from_this.hpp>
#include <boost\bind.hpp>
#include <boost\asio.hpp>
class AsyncConnection : public boost::enable_shared_from_this<AsyncConnection>
{
public:
typedef boost::shared_ptr<AsyncConnection> Pointer;
explicit AsyncConnection(boost::asio::ip::tcp::socket& socket);
virtual ~AsyncConnection();
virtual void BeginReceive();
virtual void EndReceive(const boost::system::error_code& error, std::size_t bytes_transferred);
boost::asio::ip::tcp::socket& GetSocket();
private:
boost::asio::ip::tcp::socket m_socket;
char buffer[1024];
};
#endif
#include "AsyncConnection.h"
AsyncConnection::AsyncConnection(boost::asio::ip::tcp::socket& socket)
: m_socket(socket)
{
}
AsyncConnection::~AsyncConnection()
{
}
boost::asio::ip::tcp::socket& AsyncConnection::GetSocket(){
return m_socket;
}
void AsyncConnection::BeginReceive(){
boost::asio::async_read(socket, boost::asio::buffer(buffer),
boost::asio::transfer_at_least(1),
boost::bind(&AsyncConnection::EndReceive, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void AsyncConnection::EndReceive(const boost::system::error_code& error, std::size_t bytes_transferred){
if (!error){
}
else{
std::cout << error.message() << std::endl;
}
}
IAsyncConnectionFactory
#ifndef _IASYNCCONNECTIONFACTORY_H_
#define _IASYNCCONNECTIONFACTORY_H_
#include "AsyncConnection.h"
class IAsyncConnectionFactory
{
public:
IAsyncConnectionFactory();
virtual ~IAsyncConnectionFactory();
virtual AsyncConnection::Pointer Create(boost::asio::ip::tcp::socket& socket) = 0;
};
#endif
AsyncServer
#pragma once
#ifndef _ASYNCSERVER_H_
#define _ASYNCSERVER_H_
#include <string>
#include <boost/bind.hpp>
#include "IAsyncConnectionFactory.h"
using boost::asio::ip::tcp;
class AsyncServer
{
public:
AsyncServer(boost::asio::io_service& io_service, std::string ip, unsigned short port, boost::shared_ptr < IAsyncConnectionFactory> factory);
~AsyncServer();
void BeginAccept();
void EndAccept(AsyncConnection::Pointer connection, const boost::system::error_code& error);
private:
boost::shared_ptr < IAsyncConnectionFactory> m_factory;
tcp::acceptor acceptor;
};
#endif
#include "AsyncServer.h"
AsyncServer::AsyncServer(boost::asio::io_service& io_service, std::string ip, unsigned short port, boost::shared_ptr< IAsyncConnectionFactory> factory)
: acceptor(io_service, tcp::endpoint(boost::asio::ip::address_v4::from_string(ip), port)), m_factory(factory)
{
BeginAccept();
}
AsyncServer::~AsyncServer()
{
}
void AsyncServer::BeginAccept(){
AsyncConnection::Pointer new_connection = m_factory->Create(boost::asio::ip::tcp::socket(acceptor.get_io_service()));
acceptor.async_accept(new_connection->GetSocket(),
boost::bind(&AsyncServer::EndAccept, this, new_connection, boost::asio::placeholders::error));
}
void AsyncServer::EndAccept(AsyncConnection::Pointer connection, const boost::system::error_code& error){
if (!error)
{
connection->BeginReceive();
}
else
{
std::cout << error.message() << std::endl;
}
BeginAccept();
}
Your GetSocket member-function attempts to return tcp::socket by value. This is impossible, since tcp::socket cannot be copied.
You can return tcp::socket &, but make sure to avoid dangling references.
Update: if you still get this error, make sure that you never attempt to copy an object, which contains an Asio object. To get better diagnostics, make copy-ctor and operator = private in AsyncConnection and AsyncServer - then the compiler will point exactly to the place where such copying takes place.
Update2: I missed another point: your AsyncConnection constructor attempts to copy tcp::socket. Either store the socket by reference or use move semantics (in C++11).

How can I setup a deadline_timer in this environment?

I am a bit lost in a construct of libraries, which I have to tangle together. I need help to indtroduce some timers into this construct.
I have the following:
com.cpp which has main and includes com.hpp
com.hpp which includes a host.h and needed boost includes and defines a class comClient
host.c with included host.h
wrapper.cpp with included com.hpp and some needed boost includes
Now, my com.cpp is creating a comClient and uses it for asynch communication on the com-port. Using boost::asio::serial_port and boost::asio::io_service.
I need to work with some timers, in order to catch when a paket needed too long to transmit.
When creating an instance of comClient, the paket-timer should be initialised.
Using asynch_read_some in a private function of comClient, I call a private handler of comClient, then this handler calls a function of host.c, which calls to the wrapper.cpp a function to restart the timer.
This is the function to init the timer:
//wrapper.cpp
void IniPacketTimer(void *pCHandle){
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::milliseconds(25));
t.async_wait(&hostOnTimeout(pCHandle));
io.run();
}
This would be the command chain in short:
//comClient.cpp
main{
comClient cc();
}
//comClient.hpp
class comClient(boost::asio::io_service& io_service){
comClient(){
hostInit();
aread();
}
private:
aread( call aread_done)
areaddone(call hostNewData())
}
//host.c
hostInit(){
IniPacketTimer()
}
hostNewData(){
resetTimer
}
//wrapper.cpp
resetTimer(){
t.expires_from_now
}
Questions:
How can I provide an asynchronous timer, which does not affect the asynch read/write operations on my serial port, but triggers execution of a function when the deadline is hit?
Should I use the already existing io_service or is it ok, if I just create another?
Why do I get an error C2102 '&' expects L-Value for my line t.async_wait?
You problem is not clear and since you don't post real code it is quite hard to guess what your problem is.
Especially your threading is not clear but for asio very important.
Below is an example that will compile but not run. I hope it gives you an hint on how to proceed.
It will open a serial port and a timer. Whenever the timer expires it will start a new one. It is a stripped version of code I used some time ago so maybe it will help you.
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <vector>
class SerialCommunication
{
public:
SerialCommunication(boost::asio::io_service& io_service, const std::string& serialPort)
: m_io_service(io_service)
, m_serialPort(m_io_service)
, m_timeoutTimer(m_io_service, boost::posix_time::milliseconds(5))
{
configureSerialPort(serialPort);
}
void configureSerialPort(const std::string& serialPort)
{
if(m_serialPort.is_open())
{
m_serialPort.close();
m_timeoutTimer.cancel();
}
boost::system::error_code ec;
m_serialPort.open(serialPort, ec);
if(m_serialPort.is_open())
{
// start Timer
m_timeoutTimer.async_wait(boost::bind(&SerialCommunication::TimerExpired, this, _1));
header_sync();
}
}
void header_sync()
{
m_serialPort.async_read_some(boost::asio::buffer(&m_header.back(), 1),
boost::bind(&SerialCommunication::header_sync_complete, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void header_sync_complete(
const boost::system::error_code& error, size_t bytes_transferred)
{
// stripped
read_payload(&m_payload[0], 0);
}
void read_payload(uint8_t* buffer, uint8_t length)
{
m_serialPort.async_read_some(boost::asio::buffer(buffer, length),
boost::bind(&SerialCommunication::payload_read_complete, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void payload_read_complete(
const boost::system::error_code& error, size_t bytes_transferred)
{
// stripped
// timer cancel and reset
m_timeoutTimer.cancel();
m_timeoutTimer.expires_at(boost::posix_time::microsec_clock::local_time() +
boost::posix_time::milliseconds(5));
m_timeoutTimer.async_wait(boost::bind(&SerialCommunication::TimerExpired, this, _1));
memset(&m_header[0], 0, 3);
header_sync();
}
void TimerExpired(const boost::system::error_code& e)
{
m_timeoutTimer.expires_at(m_timeoutTimer.expires_at() + boost::posix_time::milliseconds(5));
m_timeoutTimer.async_wait(boost::bind(&SerialCommunication::TimerExpired, this, _1));
}
boost::asio::io_service& m_io_service;
boost::asio::deadline_timer m_timeoutTimer;
boost::asio::serial_port m_serialPort;
std::vector<uint8_t> m_header;
std::vector<uint8_t> m_payload;
};
int main()
{
boost::asio::io_service io_service;
SerialCommunication cc(io_service, "/dev/ttyS0");
io_service.run();
return 0;
}

boost::asio::async_write from outside class

If programming a tcp server using boost.asio using the example of the echo server , i have modified some of its codes to meet my requirements where i want to process the incoming data and send back the results, i used a class for socket handling "socket.h" and i want to make another handler in for the data in the file "handler.h" , my problem now is how can i pass data to the function in handler.h and send data back from this function passing through socket.h ??
socket.h
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <json/json.h>
#include "handler.h"
using namespace std;
using boost::asio::ip::tcp;
class session {
public:
session(boost::asio::io_service& io_service) : socket_(io_service) {}
tcp::socket& socket() { return socket_; }
/* listen for first input data after connection established */
void start() {
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handleIncome,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)); }
/* handle incoming data */
void handleIncome(const boost::system::error_code& error, size_t bytes_transferred) {
/* data is recieved in var data_ */
if (!error) {
/********************* Data Handler ****************************/
callHandler(data_); //this is in handler.cpp
/**************************************************************/
} else { delete this; } }
/* get more input */
void getIncome(const boost::system::error_code& error) {
if (!error) {
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handleIncome, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred)); }
else { delete this; } }
/* send outcome back to client */
void sendOutcome(const std::string dout, size_t bytes_out) {
boost::asio::async_write(socket_,boost::asio::buffer(dout, bytes_out),
boost::bind(&session::getIncome, this,boost::asio::placeholders::error)); }
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class DServer {
public:
DServer(boost::asio::io_service& io_service, short port)
:io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&DServer::handle_accept,this,new_session,boost::asio::placeholders::error));
}
void handle_accept(session* new_session,const boost::system::error_code& error) {
if (!error) {
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),boost::bind(&DServer::handle_accept, this, new_session,boost::asio::placeholders::error));}
else { delete new_session; } }
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
handler.cpp
void callHandler(string data) {
/* here i want to process data and after that i want to send back the result to the same client ofcourse using the function sendOutcome() in the socket.h file */
}
The most common way to return data from a function is by returning it:
string callHandler(string data);
sendOutcome(callHandler(data_));
If you need more flexibility (e.g. to send multiple responses, or to do something else with the socket), then either pass a reference to the socket, or pass a reference to the session object (perhaps using an abstract interface to decouple it from the class implementation).
First of all, you must make sure, that you have received all the data you need. Your handler should cope with a scenario where the handleIncome() callback is called with bytes_transferred being 1 even if the entire request is much larger.
And of cause, you should not use the read callback as parameter to the async_write() function.

libboost ASIO. Simple asynchronous client server

I'm trying to implement a simple client/server in ASIO.
I'd like the following on the serverside:
onConnect()
onDisconnect()
onMessageRecieved(char* data)
sendMessage(char* data)
and on the client side:
onConnect()
onDisconnect()
onMessageRecieved(char* data)
sendMessage(char* data)
I didn't realise things would be so complicated.
Here's the simple echo server which I'm working off of:
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session
{
public:
session(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error)
{
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(&session::handle_write, this,
boost::asio::placeholders::error));
}
else
{
delete this;
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server
{
public:
server(boost::asio::io_service& io_service, short port)
: io_service_(io_service),
acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
{
session* new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
void handle_accept(session* new_session,
const boost::system::error_code& error)
{
if (!error)
{
new_session->start();
new_session = new session(io_service_);
acceptor_.async_accept(new_session->socket(),
boost::bind(&server::handle_accept, this, new_session,
boost::asio::placeholders::error));
}
else
{
delete new_session;
}
}
private:
boost::asio::io_service& io_service_;
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_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;
}
I can telnet into this server and everything is echoed.
Now I'd like to wrap up this code in onConnect(), onDisconnect(), onMessageReceived(char* data), etc. Similar to the way things are done in Node.js!
Has anyone got any pointers in this regard?
onMessageReceived() can be called from handle_read.
onConnect() can be called from start.
onDisconnect() can be called in the destructor of the session class.
For the bounty questions:
The io_service.run() can be placed in its own thread.
As per the documentation
Certain guarantees are made on when the handler may be invoked, in particular that a handler can only be invoked from a thread that is currently calling run() on the corresponding io_service object.
Asynchronous sending and receiving can be handled by this single thread. This simplifies thread safety because all the callbacks will be running in succession. This is probably the simplest way of using boost asio.
For calls coming from outside of the run() thread, you can schedule a callback (e.g. deadline_timer), from the 'outside thread' for immediate calling to simplify your thread safety handling. e.g.
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(0));
timer.async_wait(boost::bind(&MyClass::MyCallback, this, boost::asio::placeholders::error);
The io_service object will call the handler for you in a thread-safe fashion as soon as it has a chance. This way, your asio code can behave as if there was only a single thread in the entire system.
If multiple threads are required or preferred (e.g. Take advantage of multi-core) you may call run() on multiple thread. Handlers will have to be re-entrant. You may also want to use a strand for certain operations.
Otherwise, regular thread safety rules applies.