I have class where I use boost asio library:
Header:
class TestIOService {
public:
void makeConnection();
static TestIOService getInst();
private:
TestIOService(std::string address);
std::string address;
// boost::asio::io_service service;
};
Impl:
#include <boost/asio/ip/address.hpp>
#include <boost/asio/ip/udp.hpp>
#include "TestIOService.h"
void TestIOService::makeConnection() {
boost::asio::io_service service;
boost::asio::ip::udp::socket socket(service);
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string("192.168.1.2"), 1234);
socket.connect(endpoint);
socket.close();
}
TestIOService::TestIOService(std::string address) : address(address) { }
TestIOService TestIOService::getInst() {
return TestIOService("192.168.1.2");
}
And main:
int main(void)
{
TestIOService service = TestIOService::getInst();
service.makeConnection();
}
When I have service defined in makeConnection method with this line:
boost::asio::io_service service;
there is no problem, but when I have it as class field member(commented out in code) I get this error:
note: ‘TestIOService::TestIOService(TestIOService&&)’ is implicitly
deleted because the default definition would be ill-formed:
class TestIOService {
io_service is not copyable.
You can make it shared quickly by wrapping it in shared_ptr<io_service>, but you should really reconsider the design first.
If your class needs to be copyable, it would logically not contain the io_service object
E.g. the following sample does create two instances of the test class not sharing a connection:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <iostream>
class TestIOService {
public:
void makeConnection();
static TestIOService getInst();
private:
TestIOService(std::string address);
std::string address;
boost::shared_ptr<boost::asio::ip::udp::socket> socket;
boost::shared_ptr<boost::asio::io_service> service;
};
void TestIOService::makeConnection() {
using namespace boost::asio;
service = boost::make_shared<io_service>();
socket = boost::make_shared<ip::udp::socket>(*service);
socket->connect({ip::address::from_string("192.168.1.2"), 1234 });
//socket->close();
}
TestIOService::TestIOService(std::string address)
: address(address) { }
TestIOService TestIOService::getInst() {
return TestIOService("192.168.1.2");
}
int main() {
auto test1 = TestIOService::getInst();
auto test2 = TestIOService::getInst();
}
Related
i have this socket-tcp.h with an wrapper of socket implementation using boost asio:
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
static const std::string PORT = "65432";
static const std::string HOST = "127.0.0.1";
struct Client
{
boost::asio::io_service& io_service;
boost::asio::ip::tcp::socket socket;
Client(boost::asio::io_service& svc, std::string const& host, std::string const& port)
: io_service(svc), socket(io_service)
{
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port));
boost::asio::connect(this->socket, endpoint);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
boost::asio::io_service svc;
Client client(svc, HOST, PORT);
class TcpSocket
{
private:
std::string HOST;
std::string PORT;
public:
TcpSocket (std::string const& host, std::string const& port): HOST{ host },PORT{ port }
{
}
void send(std::string const& message)
{
boost::thread t(client_thread,boost::ref(message),boost::ref(HOST),boost::ref(PORT));
t.join();
}
static void client_thread(std::string const& message,std::string const& host,std::string const& port)
{
client.send(message);
}
};
so that my main file looks like:
#include "socket-tcp.h"
int main()
{
TcpSocket socket(PORT,HOST);
std::string message = "socket implemented using global variables";
while (true)
{
socket.send(message);
}
}
I'm trying to figure out a way of implement this without the global variables
boost::asio::io_service svc;
Client client(svc, HOST, PORT);
such that TcpSocket be like:
class TcpSocket
{
//Object* myObject; // Will not try to call the constructor or do any initializing
//myObject = new Object(...); // Initialised now
private:
std::string HOST;
std::string PORT;
boost::asio::io_service svc;
public:
TcpSocket (std::string const& host, std::string const& port): HOST{ host },PORT{ port }
{
Client client(svc, HOST, PORT);
}
void send(std::string const& message)
{
boost::thread t(client_thread,boost::ref(message),boost::ref(HOST),boost::ref(PORT));
t.join();
}
static void client_thread(std::string const& message,std::string const& host,std::string const& port)
{
client.send(message);
}
};
but i end up with the runtime error:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): resolve: Service not found
is there a way to avoid using these global variables (objects), and keeping the same socket open all the time, without closing and opening it again at each new message?
I accept better implementations or suggestions for this wrapper, but the goal is to keep main as simple and clear as possible.
The "service not found" merely means that the port is not a valid service. That's because you swapped host and port parameters.
But that is the least of your worries.
You're playing fast and loose with object lifetimes. For example, you pass message all the way to another thread &&by reference** (std::string const&), but the object referenced lives on the stack (as a function argument) so that invokes Undefined Behaviour.
Besides, it's not clear how the thread (or client_thread) is supposed to access the Client instance (that your code constructs as a local variable in the TcpSocket constructor only). Let alone that it would be unsafe to share that client across threads.
Medium Fix
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
static const std::string HOST = "127.0.0.1";
static const std::string PORT = "65432";
using boost::asio::ip::tcp;
struct Client
{
tcp::socket socket;
//template <typename Executor>
using Executor = boost::asio::io_service&;
Client(Executor executor, std::string const& host, std::string const& port)
: socket(executor)
{
std::cout << std::quoted(host) << std::endl;
std::cout << std::quoted(port) << std::endl;
auto ep = tcp::resolver{socket.get_executor()}.resolve(host, port);
connect(socket, ep);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
class TcpSocket {
private:
std::string _host;
std::string _port;
boost::asio::io_service _svc;
public:
TcpSocket(std::string const& host, std::string const& port)
: _host{host}
, _port{port}
{
}
void send(std::string const& message)
{
boost::thread t( //
client_thread, boost::ref(_svc), message, _host, _port);
t.join();
}
static void client_thread( //
boost::asio::io_service& svc,
std::string message, // NOT A REFERENCE
std::string host, // same
std::string port)
{
Client client(svc, host, port);
client.send(message);
}
};
//#include "socket-tcp.h"
int main()
{
TcpSocket socket(HOST, PORT);
std::string const message = "socket implemented using global variables";
while (true) {
socket.send(message);
}
}
More Notes
The thread is by definition useless, since you join it immediately. But if you really wanted that, please consider c++11 style:
void send(std::string const& message)
{
boost::thread t([=, &_svc] {
Client client(_svc, _host, _port);
client.send(message);
});
t.join();
}
It makes far more sense to do it on the main thread:
void send(std::string const& message)
{
Client client(_svc, _host, _port);
client.send(message);
}
Or to use async IO. Note that you should probably prefer to pass executors rather than sharing references to an execution context. Also, prefer io_context because io_service is deprecated.
Here's a simplified program that still does the same: Live On Coliru
BONUS - Async With A Thread Pool
Seems like you wanted the messages to be delivered in async fashion. However, you didn't know how to get threads in the background. Regardless, it wouldn't be a safe idea to "just create a new thread" each time. The professional approach is to use a thread pool.
This example uses a ddefault boost::asio::thread_pool and throws in a strand (even though it's not currently required) and a Session object to hold all the state for a single message. Note that we now cache the resolver results. That can be a good thing ,or a bad thing depending on your application.
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/enable_shared_from_this.hpp>
using boost::asio::ip::tcp;
using boost::system::error_code;
class TcpClient {
public:
TcpClient(std::string const& host, std::string const& port)
: _ep(tcp::resolver{_io}.resolve(host, port))
{ }
void send(std::string message)
{
boost::make_shared<Session>( //
make_strand(_io.get_executor()), std::move(message), _ep)
->run();
}
~TcpClient() {
//_io.stop(); // optionally
_io.join();
}
private:
boost::asio::thread_pool _io;
tcp::resolver::results_type _ep ;
struct Session : boost::enable_shared_from_this<Session> {
Session(auto executor, std::string message,
tcp::resolver::results_type ep)
: _socket(executor)
, _message(std::move(message))
, _ep(ep)
{
}
void run() {
async_connect( //
_socket, _ep,
[this, self = shared_from_this()](error_code ec,
tcp::endpoint) {
async_write(_socket, boost::asio::buffer(_message),
[this, self](error_code ec, size_t) {});
});
}
tcp::socket _socket;
std::string _message;
tcp::resolver::results_type _ep;
};
};
//#include "socket-tcp.h"
int main()
{
TcpClient socket("127.0.0.1", "65432");
for (int i = 0; i<100; ++i) {
socket.send("socket implemented using async IO on thread pool " +
std::to_string(i) + "\n");
}
}
I created a server using Boost ASIO. It builds fine but as soon as I run it, it gives segmentation fault. Can't really figure out this behaviour.
Also, I read that this may be due to me not initialising the io_service object explicitly. If, that's the case then how do I modify this code so that I don't have to pass io_service object from outside the class.
Below is my code:
#include <iostream>
#include <string>
#include <memory>
#include <array>
#include <boost/asio.hpp>
using namespace boost::asio;
//Connection Class
class Connection : public std::enable_shared_from_this<Connection>{
ip::tcp::socket m_socket;
std::array<char, 2056> m_acceptMessage;
std::string m_acceptMessageWrapper;
std::string m_buffer;
public:
Connection(io_service& ioService): m_socket(ioService) { }
virtual ~Connection() { }
static std::shared_ptr<Connection> create(io_service& ioService){
return std::shared_ptr<Connection>(new Connection(ioService));
}
std::string& receiveMessage() {
size_t len = boost::asio::read(m_socket, boost::asio::buffer(m_acceptMessage));
m_acceptMessageWrapper = std::string(m_acceptMessage.begin(), m_acceptMessage.begin() + len);
return m_acceptMessageWrapper;
}
void sendMessage(const std::string& message) {
boost::asio::write(m_socket, boost::asio::buffer(message));
}
ip::tcp::socket& getSocket(){
return m_socket;
}
};
//Server Class
class Server {
ip::tcp::acceptor m_acceptor;
io_service m_ioService ;
public:
Server(int port):
m_acceptor(m_ioService, ip::tcp::endpoint(ip::tcp::v4(), port)){ }
virtual ~Server() { }
std::shared_ptr<Connection> createConnection(){
std::shared_ptr<Connection> newConnection = Connection::create(m_ioService);
m_acceptor.accept(newConnection->getSocket());
return newConnection;
}
void runService() {
m_ioService.run();
}
};
int main(int argc, char* argv[]) {
Server s(5000);
auto c1 = s.createConnection();
//do soething
s.runService();
return 0;
}
You are facing initialisation order issues. In your class Server, you have declared m_acceptor before m_ioService and using the uninitialized io_service object to construct the acceptor.
Just reorder the declarations inside the class. Surprisingly clang did not give any warning for this.
class Server {
io_service m_ioService ;
ip::tcp::acceptor m_acceptor;
public:
Server(int port):
m_acceptor(m_ioService, ip::tcp::endpoint(ip::tcp::v4(), port)){ }
virtual ~Server() { }
std::shared_ptr<Connection> createConnection(){
std::shared_ptr<Connection> newConnection = Connection::create(m_ioService);
m_acceptor.accept(newConnection->getSocket());
return newConnection;
}
void runService() {
m_ioService.run();
}
};
I want to create and connect to an unix domain socket of type SOCK_SEQPACKET by specifying the path name of the socket endpoint, but this fails to compile in boost::asio v1.60:
using namespace boost::asio::generic;
seq_packet_protocol proto{AF_UNIX, IPPROTO_SCTP}; // SOCK_SEQPACKET
seq_packet_protocol::socket sock(io_service, proto);
boost::asio::local::basic_endpoint<seq_packet_protocol> ep("/tmp/socket");
sock.connect(ep); // does not compile
do you know how to properly create an unix domain socket?
I suggest to keep it simple:
#include <boost/asio.hpp>
int main() {
boost::asio::io_service io_service;
using boost::asio::local::stream_protocol;
stream_protocol::socket s(io_service);
s.connect("/tmp/socket");
}
No doubt you can go more lowlevel, but I'm not sure when you'd need that.
UPDATE Mimicking the pre-defined stream_protocol, here's how to define seqpacket_protocol:
Live On Coliru
namespace SeqPacket {
using namespace boost::asio::local;
struct seqpacket_protocol
{
int type() const { return IPPROTO_SCTP; }
int protocol() const { return 0; }
int family() const { return AF_UNIX; }
typedef basic_endpoint<seqpacket_protocol> endpoint;
typedef boost::asio::basic_stream_socket<seqpacket_protocol> socket;
typedef boost::asio::basic_socket_acceptor<seqpacket_protocol> acceptor;
#if !defined(BOOST_ASIO_NO_IOSTREAM)
/// The UNIX domain iostream type.
typedef boost::asio::basic_socket_iostream<seqpacket_protocol> iostream;
#endif // !defined(BOOST_ASIO_NO_IOSTREAM)
};
}
Just use it in the same pattern:
int main() {
boost::asio::io_service io_service;
using SeqPacket::seqpacket_protocol;
seqpacket_protocol::socket s(io_service);
s.connect("socket");
}
To add on to sehe's answer, the socket definition is wrong, it should be:
typedef boost::asio::basic_seq_packet_socket<seqpacket_protocol> socket;
putting it together:
namespace SeqPacket {
struct seqpacket_protocol
{
int type() const { return SOCK_SEQPACKET; }
int protocol() const { return 0; }
int family() const { return AF_UNIX; }
typedef boost::asio::local::basic_endpoint<seqpacket_protocol> endpoint;
typedef boost::asio::basic_seq_packet_socket<seqpacket_protocol> socket;
typedef boost::asio::basic_socket_acceptor<seqpacket_protocol> acceptor;
#if !defined(BOOST_ASIO_NO_IOSTREAM)
/// The UNIX domain iostream type.
typedef boost::asio::basic_socket_iostream<seqpacket_protocol> iostream;
#endif // !defined(BOOST_ASIO_NO_IOSTREAM)
};
}
I'm trying to save the result of bind to std:function, then pass it as parameter to another function, and store it as data member. Then I use asio async_wait, but when i return from the wait, and try to operate the function i saved i get segmentation fault. any Idea why?
#include <memory>
#include <iostream>
#include <asio/io_service.hpp>
#include <functional>
#include <asio/deadline_timer.hpp>
using namespace std;
typedef std::function<void (const std::error_code& error)> TM_callback;
class Timer {
public:
Timer(asio::io_service& io_service) :_timer(io_service) {}
void start(TM_callback cb) {
_cb = cb;
_timer.expires_from_now(boost::posix_time::milliseconds(1000));
TM_callback timeoutFunc = std::bind(&Timer::onTimeout, this, std::placeholders::_1);
_timer.async_wait(timeoutFunc);
}
private:
void onTimeout(const std::error_code& error) {
(_cb)(error); // <-- here i get segmentation fault
}
TM_callback _cb;
asio::deadline_timer _timer;
};
class COL {
public:
COL(asio::io_service& io_service): _inTimer(io_service){}
void startInTimer() {
TM_callback cb = std::bind(&COL::onInTimeout, this, std::placeholders::_1);
_inTimer.start(cb);
}
private:
void onInTimeout(const std::error_code& error) {cout<<error.message();}
Timer _inTimer;
};
int main()
{
asio::io_service io_service;
COL col(io_service);
col.startInTimer();
return 0;
}
Ok, the most likely problem is in the code you don't show. As you can see #m.s. didn't "imagine" your problem. He forgot the io_service::run() too:
int main() {
asio::io_service io_service;
COL col(io_service);
col.startInTimer();
io_service.run();
}
Still no problem. Live On Coliru
The problem starts when inTimer is not guaranteed to live until the completion handler is executed:
int main() {
asio::io_service io_service;
{
COL col(io_service);
col.startInTimer();
}
io_service.run();
}
Now you have Undefined Behaviour: Live On Coliru
Solution
The easiest solution is to make the COL (what is that?) object live long enough. The more structural/idiomatic way would to let the bind keep the Timer object alive, e.g. using a shared_ptr:
Live On Coliru
#include <iostream>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/asio.hpp>
using namespace std;
typedef std::function<void(const boost::system::error_code &error)> TM_callback;
namespace asio = boost::asio;
class Timer : public boost::enable_shared_from_this<Timer> {
public:
Timer(asio::io_service &io_service) : _timer(io_service) {}
void start(TM_callback cb) {
_cb = cb;
_timer.expires_from_now(boost::posix_time::milliseconds(1000));
TM_callback timeoutFunc = boost::bind(&Timer::onTimeout, shared_from_this(), boost::asio::placeholders::error);
_timer.async_wait(timeoutFunc);
}
private:
void onTimeout(const boost::system::error_code &error) {
(_cb)(error);
}
TM_callback _cb;
asio::deadline_timer _timer;
};
class COL : public boost::enable_shared_from_this<COL> {
public:
COL(asio::io_service &io_service) : _svc(io_service) {}
void startInTimer() {
TM_callback cb = boost::bind(&COL::onInTimeout, shared_from_this(), boost::asio::placeholders::error);
boost::shared_ptr<Timer> _inTimer = boost::make_shared<Timer>(_svc);
_inTimer->start(cb);
}
private:
void onInTimeout(const boost::system::error_code &error) { cout << error.message(); }
asio::io_service& _svc;
};
int main() {
asio::io_service io_service;
{
boost::make_shared<COL>(io_service)->startInTimer();
}
io_service.run();
}
Note that this subtly also fixes the problem that more than one timer couldn't be in flight at a give time (scheduling a new timer would cancel the pending one).
I already asked about my Problem, now I'm on the next Step. In the code below I have the Problem, that I always have to make the EventHandler (Server::HandleMessage) static. But I need to have it non static to access other Variables in the Server class from within the Handler.
How can I achieve this?
Here my Code:
#include <iostream>
#include <functional>
using namespace std;
class Client{
public:
struct MessageReceiveArgs {
MessageReceiveArgs(int ID, const std::string& Text) : ID(ID), Text(Text) {}
int ID;
std::string Text;
};
std::function<void(MessageReceiveArgs)> onMessageReceive;
Client(){}
void FireEvent(){
this->onMessageReceive(MessageReceiveArgs(16, "SomeText"));
}
};
class Server{
public:
int i;
Server(){
this->client.onMessageReceive = &Server::HandleMessage;
this->i = 5;
}
void FireEvent(){
this->client.FireEvent();
}
Client client;
static void HandleMessage(Client::MessageReceiveArgs args) {
std::cout<<"ID "<<args.ID<<": "<<" "<<args.Text<<std::endl;
//need it non static
//std::cout<<"I: "<<this->i<<std::endl;
}
};
int main() {
Server sv = Server();
sv.FireEvent();
}
As mentioned in my earlier Post, i'm new to Standard C++ (Unix).
I'm fairly sure this is what you're after. You need to bind the implicit this explicitly when invoking a pointer-to-member through std::function in the fashion you seem to desire.
#include <iostream>
#include <functional>
using namespace std;
class Client{
public:
struct MessageReceiveArgs
{
MessageReceiveArgs(int ID, const std::string& Text)
: ID(ID), Text(Text) {}
int ID;
std::string Text;
};
Client(){}
void FireEvent()
{
this->onMessageReceive(MessageReceiveArgs(16, "SomeText"));
}
std::function<void(MessageReceiveArgs)> onMessageReceive;
};
class Server
{
public:
int i;
Server()
{
this->client.onMessageReceive
= std::bind(&Server::HandleMessage, this, std::placeholders::_1);
this->i = 5;
}
void FireEvent()
{
this->client.FireEvent();
}
Client client;
void HandleMessage(Client::MessageReceiveArgs args)
{
std::cout<<"ID "<<args.ID<<": "<<" "<<args.Text<<std::endl;
}
};
int main()
{
Server sv = Server();
sv.FireEvent();
}
Output
ID 16: SomeText