3

I have a server that uses asynchronous sockets from boost with an "accept" function as follows ("User" is pretty much equivalent of "Session" in this example):

    accept()
{
    this->_acceptor.async_accept(this->_socket, [this](boost::system::error_code error)
    {
        if (!error)
        {
            make_shared<User>(move(_socket), *this)->read();
        }
        accept();
    });
}

A "read" function as follows (_readData is a char array of 1024):

    void User::read()
{
    auto self(shared_from_this());
    this->_socket.async_read_some(boost::asio::buffer(this->_readData, 1024),
        [this, self](boost::system::error_code error, size_t length)
    {
        if (!error)
        {
            this->buildReceivedMessage(string(this->_readData, length));
            this->read();
        }
    });
}

And a "write" function.

When I connect with a client to the server, the first transmission from the client to the server goes smoothly, and the server sends back a response as expected. Then, I go step by step in the code, and after the read function finishes, the code goes into several header files I do not recognize, and eventually throws a "Expression: cannot dereference string iterator because the iterator was invalidated (e.g. reallocation occurred, or the string was destroyed)" assertion problem form the xstring file. And for the life of me, I do not know why.

EDIT 1: The buildReceivedMessage creates a ReceivedMessage, which contains the values of the request in an easy-to-access format, and then forwards it to a messageHandler, which forwards the request according to its type. For instance, the first request is a sign in request, so ReceivedMessage ends up in this function:

void User::handleSignin(shared_ptr<ReceivedMessage> msg)
{
    vector<string> values = msg->getValues();
    if (this->_server.getDB().isUserAndPassMatch(values[0], values[1]))
    {
        if (this->getUserByName(values[0]))
        {
            this->write("1022");
        }
        else
        {
            this->_server.addUser(shared_ptr<User>(this));
            this->write("1020");
            this->_username = values[0];
        }
    }
    else
    {
        this->write("1021");
    }
}
  • May be in the read function - print out the length and error code on receive? May be that will give you some clues? – Nim Jun 23 '17 at 09:22
  • @Nim tried to do that, output read: "system:0 (error) 16 (length of the data, which is correct)". – ShiraMrsClever Jun 23 '17 at 09:27
  • which compiler/boost version? – Nim Jun 23 '17 at 09:39
  • @Nim boost 1.64.0, msvc complier – ShiraMrsClever Jun 23 '17 at 09:43
  • The problem is not in the code you've posted, it's most likely in the code you've not posted - what happens in the `build...` method? – Nim Jun 23 '17 at 10:27
  • `void User::buildReceivedMessage(string data) { shared_ptr msg(new ReceivedMessage(data)); this->messageHandler(msg); }` – ShiraMrsClever Jun 23 '17 at 10:48
  • The `ReceivedMessage` constructor parses the data given according to the protocol and the `messageHandler` function forwards the `ReceivedMessage` to the a handler function according to the message that was received. – ShiraMrsClever Jun 23 '17 at 10:51
  • For instance, the first message I'm sending from the client is a sign in request, so the `ReceivedMessage` ends up in the function I will attach to the question. – ShiraMrsClever Jun 23 '17 at 10:52
  • why don't you run this in gdb - and have a look at the stack trace. – Nim Jun 23 '17 at 11:53
  • I already looked at the stack trace in VS, and it led me to my main function, not even the User.cpp file. – ShiraMrsClever Jun 23 '17 at 11:57

2 Answers2

0

In:

void User::read()
{
    auto self(shared_from_this());
    this->_socket.async_read_some(boost::asio::buffer(this->_readData, 1024),
        [this, self](boost::system::error_code error, size_t length)
    {
        if (!error)
        {
            this->buildReceivedMessage(string(this->_readData, length));
            this->read();
        }
    });
}

You are depending on shared_ptr self to keep your connection alive. This is fine, but does buildReceivedMessage() create a shared_ptr to self as well?

handleSignin() does not receive a shared_ptr, which makes me suspect that your object self, which is this, really is being destroyed prematurely.

I would first try to use self instead of this in User::read(), to ensure the capture of self doesn't get optimized away (yes, that is a possibility). I'd also make sure not to rely solely on the read loop to keep the connection alive.

Michaël Roy
  • 6,338
  • 1
  • 15
  • 19
-2

I had a similar problem, some of ASIO's read/write functions takes references to mutable buffers that you provide and require that the buffer is persistent until the async function is complete. Using a string as a character buffer for example passing asio::buffer(std::string("data")) would result in this assert because the string is out of scope by the time asio processes the async function. That said, look through all your read/write functions and ensure where you pass a string as a mutable buffer, the string is persistent.

Amal K
  • 4,359
  • 2
  • 22
  • 44