1
votes

I've set up a simple async tcp server using Asio (non-boost), which pretty much follows the code used here: http://think-async.com/Asio/asio-1.11.0/doc/asio/tutorial/tutdaytime3.html

I'm experiencing an issue where attempting to access a variable of the current tcp_connection instance inside the completion handler for async_read_some/async_receive throws an error. The variable in question is simply a pointer to an instance of an encryption class that I have created. It seems that this pointer becomes invalid (address of 0xFEEEFEEE) once the completion handler is called. Here's the tcp_connection class that gets created once a connection from a client is made:

class tcp_connection
    : public enable_shared_from_this<tcp_connection> {
public:
    typedef shared_ptr<tcp_connection> pointer;

    static pointer create(asio::io_service &ios) {
        return pointer(new tcp_connection(ios));
    }

    tcp::socket &socket() {
    return socket_;
    }

    void start() {
        byte* buf = new byte[4096];

        socket_.async_receive(asio::buffer(buf, 4096), 0,
            bind(&tcp_connection::handle_receive, this,
            buf,
            std::placeholders::_1, std::placeholders::_2));
    }

private:
    tcp_connection(asio::io_service &ios)
        : socket_(ios) {
        crypt_ = new crypt();
    }

    void handle_receive(byte* data, const asio::error_code &err, size_t len) {
        cout << "Received packet of length: " << len << endl;

        crypt_->decrypt(data, 0, len);  // This line causes a crash, as the crypt_ pointer is invalid.

        for (int i = 0; i < len; ++i)
            cout << hex << setfill('0') << setw(2) << (int)data[i] << ", ";

        cout << endl;
    }

    tcp::socket socket_;
    crypt* crypt_;
};

I'm assuming this has something to do with the way Asio uses threads internally. I would have thought that the completion handler (handle_receive) would be invoked with the current tcp_connection instance, though.

Is there something I'm missing? I'm not too familiar with Asio. Thanks in advance.

1

1 Answers

1
votes

Firstly, you should use shared_from_this to prevent tcp_connection to be "collected" when there are only extant async operations:

    socket_.async_receive(asio::buffer(buf, 4096), 0,
        bind(&tcp_connection::handle_receive, shared_from_this()/*HERE!!*/, 
        buf,
        std::placeholders::_1, std::placeholders::_2));

Secondly, your tcp_connection class should implement Rule Of Three (at least cleanup crypt_ in the destructor and prohibit copy/assignment).

You also don't free up buf in your current sample.

Of course, in general, just use smart pointers for all of these.

Live On Coliru