F.M.F.
F.M.F.

Reputation: 2292

Why does this Boost TCP socket work in one method and not in another?

I want to establish a socket connection in one method and use this connection in another method of a class. While in the first method (where I establish the connection) I can read and write from and to the socket as much as I want, in the second method I always get a Bad file descriptor error. Note that I use version 1.65.1 of Boost.

I created a small reproducible example (I added some logs for clarification):

code.h

#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class SocketManager {
    tcp::socket *sock;
    boost::asio::io_service *io_service;
    tcp::acceptor *acceptor;

    public:
        virtual void initialize();  // works
        virtual void evaluate();  // fails
};

code.cc

#include "code.h"
#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;


int main() {
    SocketManager sm;
    sm.initialize();
    sm.evaluate();
    return 0;
}

void SocketManager::initialize() {
    boost::asio::io_service _io_service;
    io_service = &_io_service;

    tcp::acceptor a(*io_service, tcp::endpoint(tcp::v4(), 1337));
    acceptor = &a;

    tcp::socket sock_(*io_service);
    sock = &sock_;

    a.accept(*sock);

    char data[1024];
    boost::system::error_code error;
    size_t length = sock->read_some(boost::asio::buffer(data), error);
    if (error)
        throw boost::system::system_error(error);  // No Error here!

    boost::asio::write(*sock, boost::asio::buffer(data, length));

    cout << io_service->stopped() << endl;  // prints 0
    cout << acceptor->is_open() << endl;  // prints 1
    cout << sock->is_open() << endl;  // prints 1
}

void SocketManager::evaluate() {
    cout << io_service->stopped() << endl;  // prints 0
    cout << acceptor->is_open() << endl;  // prints 1
    cout << sock->is_open() << endl;  // prints 1

    char data[1024];
    boost::system::error_code error;
    size_t length = sock->read_some(boost::asio::buffer(data), error);
    if (error)
        throw boost::system::system_error(error);  // Error: "Bad file descriptor"
}

Output of the code above when used with a client:

root@5531547d4baf:/cpp# ./a.out 
0
1
1
0
1
1
terminate called after throwing an instance of 'boost::system::system_error'
  what():  Bad file descriptor
Aborted

Note that the code that throws the error is the exact same code in both methods (in the first one it works without any error and in the second one it throws a Bad file descriptor error).

I suspect that the io_service is no longer valid when switching to the new function, so I have already experimented with the associated poll and run functions without success.

I also tried to replace the boost::asio::buffer with a boost::asio::mutable_buffer and a boost::asio::mutable_buffer. However, the exact same code works in the first method, so I suspect that the function calls are alright!

Upvotes: 0

Views: 221

Answers (1)

G.M.
G.M.

Reputation: 12889

As per the comment sock, io_service and acceptor members of SocketManager are initialized to point to local variables within SocketManager::initialize. When that completes you then have dangling pointers.

I don't see any reason to use pointers at all here. Just make them non-pointer data members and initialize them in a constructor (untested)...

class SocketManager {
    boost::asio::io_service io_service;
    tcp::acceptor           acceptor;
    tcp::socket             sock;
public:
    SocketManager ()
      : acceptor(io_service, tcp::endpoint(tcp::v4(), 1337))
      , sock(io_service)
      {}
      ...
};

Note that the io_service member is declared first as it must be initialized before sock and acceptor.

Upvotes: 1

Related Questions