2

Situation: I'm running an asynchronous TCP server where multiple simultaneous connection from the clients is a must. In this specific question, I have a function called tcp_menu_id_receive() that receives and returns a numerical value sent by the client.

Problem: Function doesn't wait for async_read_some() and immediately returns the default value.

int tcp_menu_id_receive()
{
    auto self(shared_from_this());

    int menuid = 0;
    socket_.async_read_some(boost::asio::buffer(data_, max_length), [this, self, &menuid](boost::system::error_code ec, std::size_t length)
    {
        if (!ec)
        {
            std::string ReceivedData(data_, data_ + length);
            menuid = std::stoi(ReceivedData);
            std::cout << "!ec menuid: " << menuid << std::endl;
        }
    });
    std::cout << "non !ec menuid: " << menuid << std::endl;

    return menuid;
}

Question: How do I get `tcp_menu_id_receive' to wait for the client's data?

2 Answers2

1

That is actually what async_read_some is supposed to do. It will return to the caller inmediatelly as it is an asynchronous call. Later, the callback function will be called whenever the event happens- io_service actually manages that.

I believe that you want to use non-asynchronous read which is an synchronous read, this is, the basic_stream_socket::read_some method .

Vicente Bolea
  • 1,409
  • 16
  • 39
0

In short you want to synchronize on 1 asynchronous action in the midst of many asynchronous ones.

I'd suggest using the non-async_* version of the call. You claim that read_some doesn't work for you:

I tried read_some but it blocks other clients from connecting – Chocolate Donut 1 hour ago

I must say I'm a bit surprised here, but your code isn't self contained so I can't review/try to reproduce.

Here's a "hack" using deadline_timer that you could use to as a "signal". In all fairness this means that now you do deadline_timer::wait() synchronously, and there's no real reason to think that is fundamentally different from doing the read_some synchronously.

However, my sample is self contained, you and I can test that/how it it works. It might inspire you to get why it works, how you can amend your own code.

#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <thread>

namespace ba = boost::asio;
using ba::ip::tcp;

struct X : std::enable_shared_from_this<X> {

    X(unsigned short port) {
        socket_.connect({{}, port});
        std::cout << "connected\n";
    }

    ~X() {
        work_.reset();
        if (io_thread_.joinable())
            io_thread_.join();
    }

    int tcp_menu_id_receive()
    {
        auto self(shared_from_this());

        int menuid = 0;
        std::vector<char> buf(4);

        ba::deadline_timer sig(svc_);
        sig.expires_at(boost::posix_time::max_date_time);

        socket_.async_read_some(boost::asio::buffer(buf), [&, self](boost::system::error_code ec, std::size_t length)
        {
            std::cout << "Callback: " << ec.message() << "\n";
            if (!ec) {
                std::string ReceivedData(buf.data(), length);
                menuid = std::stoi(ReceivedData);

                sig.expires_at(boost::posix_time::min_date_time);
            }
            sig.cancel(); // just abort
        });

        // synchronous wait
        boost::system::error_code ec;
        sig.wait(ec); // ignoring error code

        // optionally check result
        if (sig.expires_at() == boost::posix_time::max_date_time)
            std::cout << "Receive error\n";

        return menuid;
    }

  private:
    ba::io_service svc_;
    boost::optional<ba::io_service::work> work_{svc_};
    std::thread io_thread_ { [this] {
        std::cout << "start io_thread_\n";
        svc_.run(); 
        std::cout << "exit io_thread_\n";
    } };
    tcp::socket socket_{svc_};
};

int main() {
    auto x = std::make_shared<X>(6767);
    std::cout << "DEBUG: menuid: " << x->tcp_menu_id_receive() << "\n";
}

I tested it on my machine using a test server:

echo 2345 | netcat -l -p 6767

And the output was:

connected
start io_thread_
Callback: Success
DEBUG: menuid: 2345
exit io_thread_
sehe
  • 374,641
  • 47
  • 450
  • 633