Skip to content

Instantly share code, notes, and snippets.

@sehe
Last active July 28, 2023 11:26
Show Gist options
  • Save sehe/5ec43b7baa7fb8a15ebf to your computer and use it in GitHub Desktop.
Save sehe/5ec43b7baa7fb8a15ebf to your computer and use it in GitHub Desktop.
Simplifying sequential use of Asio operations with timeouts
all:TCPClient
CPPFLAGS+=-std=c++11 -Wall -pedantic -g -O2 -pthread
%.o: TCPClient.hpp
%: %.o
$(CXX) $(CPPFLAGS) $^ -o $@ -lboost_system
#include <iostream>
#include "TCPClient.hpp"
int main(/*int argc, char* argv[]*/) {
TCPClient client;
try {
client.connect("localhost", "27015");
std::cout << "Response: " << client.sendMessage("Hello!") << std::endl;
}
catch (const boost::system::system_error& e) {
std::cerr << e.what() << std::endl;
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
#ifndef __TCPCLIENT_H__
#define __TCPCLIENT_H__
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include <iostream>
class TCPClient {
public:
void disconnect();
void connect(const std::string& address, const std::string& port);
std::string sendMessage(const std::string& msg);
private:
using error_code = boost::system::error_code;
template<typename AllowTime> void await_socket(AllowTime const& deadline_or_duration) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([this](error_code ec) { if (ec != error::operation_aborted) socket.cancel(); });
ioservice.run_one();
}
ioservice.run();
}
struct raise {
template <typename... A> void operator()(error_code ec, A...) const {
if (ec) throw std::runtime_error(ec.message());
}
};
boost::asio::io_service ioservice { };
boost::asio::ip::tcp::socket socket { ioservice };
};
inline void TCPClient::disconnect() {
using namespace boost::asio;
if (socket.is_open()) {
try {
socket.shutdown(ip::tcp::socket::shutdown_both);
socket.close();
}
catch (const boost::system::system_error& e) {
// ignore
std::cerr << "ignored error " << e.what() << std::endl;
}
}
}
inline void TCPClient::connect(const std::string& address, const std::string& port) {
using namespace boost::asio;
async_connect(socket, ip::tcp::resolver(ioservice).resolve({address, port}), raise());
await_socket(std::chrono::seconds(6));
}
inline std::string TCPClient::sendMessage(const std::string& msg) {
using namespace boost::asio;
streambuf response;
async_read_until(socket, response, '\n', raise());
await_socket(std::chrono::system_clock::now() + std::chrono::seconds(4));
return {std::istreambuf_iterator<char>(&response), {}};
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment