To learn about utilising coroutines in networking, I've been studing its use in Boost.Asio's chat server example. (doc/html/boost_asio/example/cpp17/coroutines_ts/chat_server.cpp - 1.76.0 ). However, the examples do not provide a client counterpart example. Now, I found that this is an adaptation of an earlier C++11 example, which does have a client example.
However, the older example uses the method of a header and body, whereas the newer example uses a read-until-newline method. I've tried rewriting the older client example to reflect that. Though both compile, I opened Wireshark to check what's being sent and received. The client is able to send the message, the server acknowledges that it receives the message, but it doesn't relay it.
Using breakpoints, I found that the server executes the async_read_until co_await, but nothing after that is ever called.
Along with the aforementioned linked example, here's the rewritten client example intended to work with it. I've been building this in VS2019 using /std:c++latest so I could leverage coroutines.
Code:
#include <iostream>
#include <boost/asio.hpp>
#include <exception>
#include <system_error>
#include <deque>
#include <cstdint>
#include <string>
#include <cstring>
#include <utility>
#include <thread>
#include <sstream>
#include <memory>
class client : public std::enable_shared_from_this<client>
{
public:
client(boost::asio::io_context &ctx,
const boost::asio::ip::tcp::resolver::results_type &eps):
m_ctx(ctx), m_sock(ctx)
{
connect(eps);
}
void connect(const boost::asio::ip::tcp::resolver::results_type &eps)
{
boost::asio::async_connect(m_sock, eps,
[this](std::error_code ec, boost::asio::ip::tcp::endpoint)
{
if (ec) std::cerr << "Connect! ... aaaaaaand missed!" << std::endl;
else
co_spawn(m_sock.get_executor(), [self = shared_from_this()]
{
return self->read();
}, boost::asio::detached);
});
}
void write(std::string &msg)
{
bool hasWork = !msgs.empty();
msgs.push_back(msg);
boost::asio::post(m_ctx, [this, hasWork]
{
if (!hasWork) do_write();
});
}
void close()
{
boost::asio::post(m_ctx, [this]()
{
m_sock.close();
});
}
private:
boost::asio::io_context &m_ctx;
boost::asio::ip::tcp::socket m_sock;
std::deque<std::string> msgs;
boost::asio::awaitable<void> read()
{
try
{
for (boost::asio::streambuf read_tmp;;)
{
co_await boost::asio::async_read_until(m_sock, read_tmp, 0, boost::asio::use_awaitable);
std::cout << &read_tmp << std::endl;
}
}
catch (std::exception &e)
{
std::cerr << e.what() << std::endl;
m_sock.close();
}
}
void do_write()
{
boost::asio::async_write(m_sock, boost::asio::buffer(msgs.front(), std::strlen(msgs.front().data())),
[this](std::error_code ec, std::size_t)
{
if (ec)
{
std::cerr << "How can you write this? There's no pictures!" << std::endl;
m_sock.close();
}
else
{
msgs.pop_front();
if (!msgs.empty()) do_write();
}
});
}
};
int main(int argc, char **argv)
{
try
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <host> <port>" << std::endl;
return 1;
}
boost::asio::io_context ctx;
boost::asio::ip::tcp::resolver res(ctx);
auto eps = res.resolve(argv[1], argv[2]);
std::shared_ptr<client> c = std::make_shared<client>(ctx, eps);
std::thread thr([&ctx]()
{
ctx.run();
});
std::string input(1024, 0);
while (std::cin.getline(input.data(), 1024))
{
input += '\n';
c->write(input);
}
c->close();
if (thr.joinable()) thr.join();
}
catch (std::exception &e)
{
std::cerr << "Ya ........ed up: " << e.what() << std::endl;
}
}