I´m trying to build a synchronous FTP client code with timeout using a thread as the timeout control. The thread will be started on every transaction and will close the sock
I've made a helper facility to do any Asio async operation "synchronously" with a timeout here, look for await_operation
:
You should be able to adapt the pattern for your sample.
It took a while since I wanted to test this with an ftp server.
Notes:
Fixing these things and using my await_operation
you'd get this:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#define TIMEOUT std::chrono::seconds(5)
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 2048 };
struct Service {
using error_code = boost::system::error_code;
template<typename AllowTime, typename Cancel> void await_operation_ex(AllowTime const& deadline_or_duration, Cancel&& cancel) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([&cancel](error_code ec) { if (ec != error::operation_aborted) std::forward<Cancel>(cancel)(); });
ioservice.run_one();
}
ioservice.run();
}
template<typename AllowTime, typename ServiceObject> void await_operation(AllowTime const& deadline_or_duration, ServiceObject& so) {
return await_operation_ex(deadline_or_duration, [&so]{ so.cancel(); });
}
boost::asio::io_service ioservice;
};
int main()
{
while(true)
{
try
{
Service service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::string address;
if (std::cin >> address) {
if (address == "END") break;
} else {
if (std::cin.eof())
break;
std::cerr << "Invalid input ignored\n";
std::cin.clear();
std::cin.ignore(1024, '\n');
continue;
}
tcp::socket s(service.ioservice);
tcp::resolver resolver(service.ioservice);
boost::asio::async_connect(s, resolver.resolve({address, "21"}), [](boost::system::error_code ec, tcp::resolver::iterator it) {
if (ec) throw std::runtime_error("Error connecting to server: " + ec.message());
std::cout << "Connected to " << it->endpoint() << std::endl;
});
service.await_operation_ex(TIMEOUT, [&]{
throw std::runtime_error("Error connecting to server: timeout\n");
});
auto receive = [&] {
boost::asio::streambuf sb;
size_t bytes;
boost::asio::async_read_until(s, sb, '\n', [&](boost::system::error_code ec, size_t bytes_transferred) {
if (ec) throw std::runtime_error("Error receiving message: " + ec.message());
bytes = bytes_transferred;
std::cout << "Received message is: " << &sb;
});
service.await_operation(TIMEOUT, s);
return bytes;
};
receive(); // banner
auto send = [&](std::string cmd) {
boost::asio::async_write(s, boost::asio::buffer(cmd), [](boost::system::error_code ec, size_t /*bytes_transferred*/) {
if (ec) throw std::runtime_error("Error sending message: " + ec.message());
});
service.await_operation(TIMEOUT, s);
};
auto ftp_command = [&](std::string cmd) {
send(cmd + "\r\n");
receive(); // response
};
//ftp_command("USER bob");
//ftp_command("PASS hello");
while (true) {
std::cout << "Enter command: ";
std::string request;
if (!std::getline(std::cin, request))
break;
ftp_command(request);
}
}
catch (std::exception const& e)
{
std::cerr << "COMMUNICATIONS ERROR " << e.what() << "\n";
}
}
return 0;
}
Which, in my test run, prints e.g.: