Reputation: 2958
I have been trying to implement a simple tcp server using boost, that accepts a client connection, and sends some information back to the client by the call of a method exposed by the server.
Here is the class I created, based on the Boost tutorials:
#define WIN32_LEAN_AND_MEAN
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
#include <ctime>
#include <iostream>
#include <string>
#include "Logger.h"
class tcp_server_connection
: public boost::enable_shared_from_this<tcp_server_connection>
{
public:
typedef boost::shared_ptr<tcp_server_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_server_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start(string message)
{
swap(m, message);
boost::asio::async_write(socket_, boost::asio::buffer(m),
boost::bind(&tcp_server_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
tcp_server_connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
Logger::Log("main.log", "New TCP Server Connection");
}
void handle_write(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (error) {
Logger::Log("main.log", "TCP Server Write Error: ");
}
}
tcp::socket socket_;
string m;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service, int port)
: acceptor_(io_service, tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), port))
{
start_accept();
}
void write(string message) {
connection->start(message);
}
private:
void start_accept()
{
connection = tcp_server_connection::create(acceptor_.get_io_service());
acceptor_.async_accept(connection->socket(),
boost::bind(&tcp_server::handle_accept, this, connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_server_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error)
{
Logger::Log("main.log", "TCP Server Accepted Connection");
}
else {
Logger::Log("main.log", "TCP Server Error accepting Connection");
}
}
tcp::acceptor acceptor_;
tcp_server_connection::pointer connection;
};
I start the server by starting a thread with this method:
void SetupServer() {
boost::asio::io_service io_service;
server = new tcp_server(io_service, serverPort);
io_service.run();
}
and I call server->write("Some Text") when I want to write something to the client, but async_write raises and exception saying "Access violation writing location". I believe that there might be some object being cleaned up before it should, but I do not understand why and where, so I would appreciate it if someone could give me some insight on why this is happening and how to solve it.
Thank you in advance for any help.
Upvotes: 1
Views: 923
Reputation: 2958
Well I figured out the issue was related to the fact that the io_service variable was being released on scope change, and hell was breaking loose.
To fix this, I changed my setupServer to this:
io_service = new boost::asio::io_service();
rankingServer = new tcp_server(*io_service, serverPort);
io_service->run();
and declaring the variable elsewhere in the class using it:
boost::asio::io_service *io_service = NULL;
Remember that the variable needs to be released, calling
delete io_service;
Hope this might help someone out.
Upvotes: 1
Reputation: 621
It sounds like you are calling server->write("Some Text") from the main thread. However, at the time point, no client might be connected so far so that you possibly try to write on a socket with no connected endpoint. It might even be the case that the io_service thread has not even started yet.
When writing asynchronous programs with boost asio, you usually start an async action and pass a handler that will be called once the requested action has succeeded (or failed). There you can react and e.g. chain another asynchronous action. In your example, once a client is successfully connected, the tcp_server::handle_accept() handler will be called with no error code set. This is the place to put the call to write("Some Text").
Based on your question, it seems to me that you want to trigger writing data from "outside" the io_service thread. Please be careful when working with multiple threads. Be sure to use appropriate locking when trying to access data from both the "main" thread and the thread running the io_service (and thus all the asynchronous handlers). Also watch out for race conditions and do not rely on timing or scheduling of your computer or OS.
Upvotes: 0