I am trying to write my socket class (code below).
At last everything works:reconnection, connection, message send, message receive
But I noticed memory leak when I repeat TCPSocketBody::Connect(const std::string &adress, const std::string &port) if for example host is unreachable. Size of my application is growing.
I discovered that when I remove line:
thread_io_service = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_global));
the problem disappears. But I need this line. What I am doing wrong?
I checked from command line number of thread in my application and it always is 1 or 2.
#include "TCPSocketBody.h"
TCPSocketBody::TCPSocketBody() : socket_(io_service_global),
resolver(io_service_global),
connected(false),
expectedMessage(0)
{
}
void TCPSocketBody::Close()
{
io_service_global.post(boost::bind(&TCPSocketBody::DoClose, this));
}
void TCPSocketBody::Connect(const std::string &adress, const std::string &port)
{
io_service_global.reset();
iterator = resolver.resolve({adress, port});
boost::asio::async_connect(socket_, iterator,
boost::bind(&TCPSocketBody::HandleConnect, this,
boost::asio::placeholders::error));
socket_.set_option(boost::asio::ip::tcp::no_delay(true));
thread_io_service = boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_global));
}
void TCPSocketBody::HandleConnect(const boost::system::error_code& error)
{
if (!error)
{
connected = true;
boost::asio::async_read(socket_,
boost::asio::buffer(data_to_read, MessageCoder::BufferSize()), boost::asio::transfer_all(),
boost::bind(&TCPSocketBody::HandleReadHeader, this,
boost::asio::placeholders::error));
} else
{
Close();
}
}
void TCPSocketBody::HandleReadHeader(const boost::system::error_code& error)
{
expectedMessage = MessageCoder::HeaderToVal(data_to_read);
if (expectedMessage > MessageCoder::MaxMessageSize())
{
expectedMessage = 0;
Close();
} else
{
boost::asio::async_read(socket_,
boost::asio::buffer(data_to_read, expectedMessage), boost::asio::transfer_all(),
boost::bind(&TCPSocketBody::HandleReadMessage, this,
boost::asio::placeholders::error));
}
}
void TCPSocketBody::HandleReadMessage(const boost::system::error_code& error)
{
expectedMessage = 0;
boost::asio::async_read(socket_,
boost::asio::buffer(data_to_read, MessageCoder::BufferSize()), boost::asio::transfer_all(),
boost::bind(&TCPSocketBody::HandleReadHeader, this,
boost::asio::placeholders::error));
}
void TCPSocketBody::WriteMessage(char *dataToSend)
{
io_service_global.post(boost::bind(&TCPSocketBody::Write, this, dataToSend));
}
void TCPSocketBody::Write(char *dataToSend)
{
data = dataToSend;
boost::asio::async_write(socket_,
boost::asio::buffer(data, std::strlen(data)),
boost::bind(&TCPSocketBody::HandleWrite, this,
boost::asio::placeholders::error));
}
void TCPSocketBody::HandleWrite(const boost::system::error_code& error)
{
if (!error)
{
}
else
{
Close();
}
}
void TCPSocketBody::DoClose()
{
socket_.close();
connected = false;
}
Without the header file, it is hard to be certain. I suspect that thread_io_service is a member variable. A second call to 'Connect' will overwrite the existing value without destroying the object. Any memory allocated within the object will be leaked.
Perhaps try making a std::vector of thread_io_service and push_back each new thread on the connect call. The vector will destroy the contained objects when it gets destructed.
Valgrind can likely pinpoint the issue with more detail.
Related
Trying to get my head around the boost classes. What I want to do, differs from the boost asio tutorial in that, the tutorial has a main() where the io_service object is instantiated. That is then passed to the class implementing asio via its constructor.
What I want to do, is to eliminate the instantiation of io_service in the main and have the implementing class be "self-contained" in that, it will declare its own io_service and socket. I must be reading the example with tunnel-vision, because I cannot figure out how to drop the instantiation of io_service into my socket class.
At first, I was getting "error C2758: 'xxx::io_service' : must be initialized in constructor base/member initializer list". So, I thought I'd do that and added "io_service(new boost::asio::io_service()), socket(io_service)" to the class initializer list. That gave me "error C2354: 'xxx::io_service' : initialization of reference member requires a temporary variable", which, after some googling, made sense.
So, my question is, how can I adjust the tutorial code to eliminate the main()?
Is this what you're after:
Move the io_service into the class (not by reference). And all usages as well.
I opted to put the join into the destructor.
#include <cstdlib>
#include <deque>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include "chat_message.hpp"
using boost::asio::ip::tcp;
typedef std::deque<chat_message> chat_message_queue;
class chat_client
{
public:
chat_client(std::string host, std::string portorservice)
: io_service_(),
thread_(boost::bind(&boost::asio::io_service::run, &io_service_)),
socket_(io_service_)
{
tcp::resolver resolver(io_service_);
tcp::resolver::query query(host, portorservice);
tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::async_connect(socket_, iterator,
boost::bind(&chat_client::handle_connect, this,
boost::asio::placeholders::error));
}
void write(const chat_message& msg)
{
io_service_.post(boost::bind(&chat_client::do_write, this, msg));
}
void close()
{
io_service_.post(boost::bind(&chat_client::do_close, this));
}
~chat_client() {
close();
if (thread_.joinable())
thread_.join();
}
private:
void handle_connect(const boost::system::error_code& error)
{
if (!error)
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
}
void handle_read_header(const boost::system::error_code& error)
{
if (!error && read_msg_.decode_header())
{
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.body(), read_msg_.body_length()),
boost::bind(&chat_client::handle_read_body, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void handle_read_body(const boost::system::error_code& error)
{
if (!error)
{
std::cout.write(read_msg_.body(), read_msg_.body_length());
std::cout << "\n";
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(&chat_client::handle_read_header, this,
boost::asio::placeholders::error));
}
else
{
do_close();
}
}
void do_write(chat_message msg)
{
bool write_in_progress = !write_msgs_.empty();
write_msgs_.push_back(msg);
if (!write_in_progress)
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
void handle_write(const boost::system::error_code& error)
{
if (!error)
{
write_msgs_.pop_front();
if (!write_msgs_.empty())
{
boost::asio::async_write(socket_,
boost::asio::buffer(write_msgs_.front().data(),
write_msgs_.front().length()),
boost::bind(&chat_client::handle_write, this,
boost::asio::placeholders::error));
}
}
else
{
do_close();
}
}
void do_close()
{
socket_.close();
}
private:
boost::asio::io_service io_service_;
boost::thread thread_;
tcp::socket socket_;
chat_message read_msg_;
chat_message_queue write_msgs_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 3)
{
std::cerr << "Usage: chat_client <host> <port>\n";
return 1;
}
chat_client c(argv[1], argv[2]);
char line[chat_message::max_body_length + 1];
while (std::cin.getline(line, chat_message::max_body_length + 1))
{
using namespace std; // For strlen and memcpy.
chat_message msg;
msg.body_length(strlen(line));
memcpy(msg.body(), line, msg.body_length());
msg.encode_header();
c.write(msg);
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
}
boost::asio::async_read doesn't call the handler and keeps receiving, am trying to receive some buffers that I don't know it's length , but max is 1024
#include "AsyncConnection.h"
AsyncConnection::AsyncConnection(boost::asio::io_service& io_service)
: socket(io_service)
{
}
AsyncConnection::~AsyncConnection()
{
}
AsyncConnection::Pointer AsyncConnection::Create(boost::asio::io_service& io_service){
return Pointer(new AsyncConnection(io_service));
}
boost::asio::ip::tcp::socket& AsyncConnection::GetSocket(){
return socket;
}
void AsyncConnection::BeginReceive(){
boost::asio::async_read(socket, boost::asio::buffer(buffer, 1024),
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;
}
}
it never calls the EndReceive , my guess is it's keeping receiving until the buffer is full?! how to fix that tho!
I've not found a specific reference to a solution to this problem. I am writing a server using a modified version of the boost asynch example 3. I am using async_read_until. when recieving the message the program calls the handle read and calls for a new thread but the streambuf is still contains the delimeter (i'm sending an xml from a different project) and immediately calls another thread up to my threadmax.
class server{
....
void server::run()
{
std::vector<boost::shared_ptr<boost::thread>> threads;
for (std::size_t i = 0; i < thread_pool_size_; ++i)
{
boost::shared_ptr<boost::thread> thread(new boost::thread(
boost::bind(&boost::asio::io_service::run, &io_service_)));
threads.push_back(thread);
}
//wait for all threads in the pool to exit.
for (std::size_t i = 0; i < threads.size(); ++i)
threads[i]->join();
}
void server::start_accept()
{
new_connection_.reset(new connection(io_service_, request_handler_));
acceptor_.async_accept(new_connection_->socket(),
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error));
}
void server::handle_accept(const boost::system::error_code& e)
{
if (!e)
{
new_connection_->start();
}
start_accept()
}
}
class connection{
....
void connection::start()
{
boost::asio::async_read_until(socket_, buffer_,
"<\\User>",
boost::bind(&connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void connection::handle_read(const boost::system::error_code& e,
std::size_t bytes_transferred)
{
if(!e)
{
std::ostringstream ss;
std::cout << ss.str();
buffer_.consume(buffer_.size());
}
}
I'm developing a 3d (first/third person) game and I'm trying to make it multiplayer using TCP sockets. I'm using the boost asio library for this, and I'm in a little over my head. I've played with the tutorials and examples a bit on the boost asio doc page and they compiled/ran/worked just fine, I'm just a little confused as to how everything works.
Right now I'm just trying to make the server accept messages from the client, and print the message after receiving it. When I execute the code below (it compiles/links/runs fine), nothing happens. More specifically, the client appears to successfully send the message, and the server never seems to receive the message.
Client code:
ClientFramework::ClientFramework() :
mResolver(mIOService)
{
}
bool ClientFramework::Initialize()
{
try
{
tcp::resolver::query query("localhost", "daytime");
tcp::resolver::iterator it = mResolver.resolve(query);
tcp::socket socket(mIOService);
boost::asio::connect(socket, it);
std::string s = "hello world";
boost::system::error_code e;
socket.write_some(boost::asio::buffer(s.c_str(), s.size()), e);
if (e)
{
throw boost::system::system_error(e);
}
} catch (std::exception& e)
{
gLog << LOG_ERROR << e.what() << "\n";
}
return true;
}
Server code:
ServerFramework::ServerFramework() :
mAcceptor(mIOService, tcp::endpoint(tcp::v4(), 13))
{
}
bool ServerFramework::Initialize()
{
mIOService.run();
StartAccept();
return true;
}
void ServerFramework::StartAccept()
{
Connection::ptr conn =
Connection::create(mAcceptor.get_io_service());
mAcceptor.async_accept(conn->Socket(),
boost::bind(&ServerFramework::HandleAccept, this, conn,
boost::asio::placeholders::error));
}
void ServerFramework::HandleAccept(Connection::ptr conn,
const boost::system::error_code& error)
{
if (!error)
{
conn->Initialize();
}
StartAccept();
}
Connection::ptr Connection::create(boost::asio::io_service& io_service)
{
return ptr(new Connection(io_service));
}
tcp::socket& Connection::Socket()
{
return mSocket;
}
void Connection::Initialize()
{
boost::asio::async_read(mSocket, boost::asio::buffer(buf, BUFFER_SIZE),
boost::bind(&Connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
Connection::Connection(boost::asio::io_service& io_service) :
mSocket(io_service)
{
}
void Connection::handle_read(const boost::system::error_code& e, size_t size)
{
std::string s(buf, size);
gLog << LOG_INFO << s << "\n";
boost::asio::async_read(mSocket, boost::asio::buffer(buf, BUFFER_SIZE),
boost::bind(&Connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
It does not look like your io_service has any work to do when you invoke run().
bool ServerFramework::Initialize()
{
mIOService.run(); // <-- you don't check the return value here
StartAccept();
return true;
}
it will return the number of handlers executed, I suspect it is zero. Try invoking it after async_accept()
bool ServerFramework::Initialize()
{
StartAccept();
mIOService.run();
return true;
}
Though, it isn't entirely obvious by your limited code snippets where you invoke ServerFramework::Initialize(). I suggest editing your question with a short, self contained, correct example that we can compile with little to no effort. Your current code will not compile without additional boilerplate stuff, like main().
I'm trying to modify the echo server example from boost asio and I'm running into problem when I try to use boost::asio::async_read_until. Here's the code:
#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()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(socket_, 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)
{
std::cout<<"handling read"<<std::endl;
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;
}
The problem is when I try to compile I get this weird error:
server.cpp: In member function ‘void session::start()’:
server.cpp:27: error: no matching function for call to ‘async_read_until(boost::asio::basic_stream_socket >&, boost::asio::mutable_buffers_1, char, boost::_bi::bind_t, boost::_bi::list3, boost::arg<1> ()(), boost::arg<2> ()()> >)’
Can someone please explain what's going on? From what I can tell the arguments to async_read_until are correct.
Thanks!
The second argument of async_read_until should be a streambuf object into which the data will be read. To put it simple, you need to pass a boost::asio::streambuf by reference, not a boost::asio::buffer by value.
There is no need to use streambufs. There are overloads that accept dynamic buffers. Dynamic is key since read_until implies the need for a buffer that can increase its size at the callee's discretion. Therefore, all you need is to replace the call to boost::asio::buffer with boost::asio::dynamic_buffer and call it on either std::string or std::vector<char>.
...
void start()
{
std::cout<<"starting"<<std::endl;
boost::asio::async_read_until(
socket_, boost::asio::dynamic_buffer(data_), ' ',
boost::bind(&session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
...
private:
tcp::socket socket_;
std::string data_;
// or
//std::vector<char> data_;
};
I would also get rid of boost::bind since it's an overkill and use a lambda (use them as much as you can)
[this](const boost::system::error_code& error, size_t bytes_transferred) {
handle_read(error, bytes_transferred);
}
const (in)correctness may also cause this type of error message.