Boost::Asio synchronous client with timeout

后端 未结 1 1922
南笙
南笙 2020-12-04 04:04

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

相关标签:
1条回答
  • 2020-12-04 04:45

    I've made a helper facility to do any Asio async operation "synchronously" with a timeout here, look for await_operation:

    • boost::asio + std::future - Access violation after closing socket

    You should be able to adapt the pattern for your sample.

    Demo

    It took a while since I wanted to test this with an ftp server.

    Notes:

    • you didn't resolve the address (effectively requiring the user to type in IP address)
    • you didn't make sure commands were closed with newline
    • you didn't handle any kind of input error

    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.:

    0 讨论(0)
提交回复
热议问题