boost socket example stuck in while loop

大兔子大兔子 提交于 2019-12-08 14:19:41

问题


I am trying to learn boost asio socket. there is one interesting example in boost web page which set a deadline time to monitor the timeout and change async io to sync io fashion. but when I remove the deadline timer the program stuck in while loop in write_line, I don't understand why the connection can be setup but the socket write is stuck. it seems that the writing never finished ,the handler never called so the "ec" never changed. Thank you in advance!!

#include <boost/asio/connect.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read_until.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/write.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
using boost::asio::deadline_timer;
using boost::asio::ip::tcp;

class client
{
public:
  client()
    : socket_(io_service_),
      deadline_(io_service_)
  {
   deadline_.expires_from_now(boost::posix_time::seconds(10)); 
   check_deadline(); // without this line which means without the this timer 
                     //async_wait, the code stuck in write_line io_service_.run_one() loop .
  }
  void connect_handler(const boost::system::error_code& error,boost::system::error_code *er)
  {
      std::cerr<<"connect handler"<<std::endl;
      *er = error;
      std::cerr<<error<<std::endl;
  }

  void connect(const std::string& host, const std::string& service)
  {
    tcp::resolver::query query(host, service);
    tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
    std::cerr<<"connect start"<<std::endl;

    boost::system::error_code ec = boost::asio::error::would_block;

    boost::asio::async_connect(socket_, iter, bind(&client::connect_handler,this,_1,&ec));

    do
    {io_service_.run_one();
    }while (ec == boost::asio::error::would_block);

    std::cerr<<"connect done"<<std::endl; // always works fine!
    if (ec || !socket_.is_open())
      throw boost::system::system_error(
          ec ? ec : boost::asio::error::operation_aborted);
  }

  void write_handler(const boost::system::error_code& error, std::size_t size,boost::system::error_code* er )
  {
      std::cerr<<"write handler "<<std::endl;
      *er=error;
      std::cerr<<error<<std::endl;
  }
  void write_line(const std::string& line)
  {
    std::cerr<<"write start"<<std::endl;
    std::string data = line + "\n";
    boost::system::error_code ec = boost::asio::error::would_block;

    boost::asio::async_write(socket_, boost::asio::buffer(data), bind(&client::write_handler,this,_1,_2,&ec));
    do
    {
        io_service_.run_one();     //stuck here without "check_deadline();" in constructor.
    }while (ec == boost::asio::error::would_block);
    std::cerr<<"write done";
    if (ec)
      throw boost::system::system_error(ec);
  }

private:
  void check_deadline()
  {
    if (deadline_.expires_at() <= deadline_timer::traits_type::now())
    {
      boost::system::error_code ignored_ec;
      socket_.close(ignored_ec);
      deadline_.expires_at(boost::posix_time::pos_infin);
       throw boost::system::system_error(ignored_ec);
    }

    deadline_.async_wait(std::bind(&client::check_deadline, this));
  }

  boost::asio::io_service io_service_;
  tcp::socket socket_;
  deadline_timer deadline_;

};

int main()
{
  try
  {
    client c,d;
    c.connect("216.58.194.164", "80");// google IP.
    c.write_line("test");
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

Without comment out line "check_deadline();" in constructor the output is :

connect start
connect handler
system:0
connect done
write start
write handler 
system:0
write done

when the line "check_deadline();" comment out in constructor, the output is :

connect start
connect handler
system:0
connect done
write start

and stuck there forever.


回答1:


The whole point of that particular example is to have timeouts on blocking operations. This is why they use the async_* flavour of functions.

If you don't need that, don't use the async_* flavour at all:

#include <boost/asio.hpp>
#include <iostream>
#include <string>

using boost::asio::ip::tcp;

class client {
  public:
    client() : socket_(io_service_) { }

    void connect(const std::string &host, const std::string &service) {
        tcp::resolver::query query(host, service);
        socket_.close();
        boost::asio::connect(socket_, tcp::resolver(io_service_).resolve(query));
    }

    std::string read_line() {
        boost::system::error_code ec;
        boost::asio::read_until(socket_, input_buffer_, '\n', ec);

        if (ec == boost::asio::error::eof)
            socket_.close();
        else if (ec)
            throw boost::system::system_error(ec);

        std::string line;
        std::getline(std::istream(&input_buffer_), line);
        return line;
    }

    void write_line(const std::string &line) {
        std::string data = line + "\n";
        boost::asio::write(socket_, boost::asio::buffer(data));
    }

  private:
    boost::asio::io_service io_service_;
    tcp::socket socket_;
    boost::asio::streambuf input_buffer_;
};

int main(int argc, char *argv[]) {
    try {
        if (argc != 4) {
            std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
            return 1;
        }

        client c;
        c.connect(argv[1], argv[2]);

        boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time();

        c.write_line(argv[3]);

        for (;;) {
            std::string line = c.read_line();

            std::cout << "Received '" << line << "'\n";

            if (line == argv[3])
                break;
        }

        boost::posix_time::ptime time_received = boost::posix_time::microsec_clock::universal_time();

        std::cout << "Round trip time: ";
        std::cout << (time_received - time_sent).total_microseconds();
        std::cout << " microseconds\n";
    } catch (std::exception &e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
}

That's a lot simpler. In fact, it's too simple in case the packets arriving contain more than 1 line at a time.

Instead of "fixing" the sample by re-complicating it, it can be ENORMOUSLY simpler:

#include <boost/asio.hpp>
#include <iostream>
#include <string>

using boost::asio::ip::tcp;

int main(int argc, char *argv[]) {
    try {
        if (argc != 4) {
            std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
            return 1;
        }

        tcp::iostream c(argv[1], argv[2]);

        boost::posix_time::ptime time_sent = boost::posix_time::microsec_clock::universal_time();

        c << argv[3] << "\n";

        std::string line;
        while (getline(c, line)) {
            std::cout << "Received '" << line << "'\n";

            if (line == argv[3])
                break;
        }

        boost::posix_time::ptime time_received = boost::posix_time::microsec_clock::universal_time();

        std::cout << "Round trip time: ";
        std::cout << (time_received - time_sent).total_microseconds();
        std::cout << " microseconds\n";
    } catch (std::exception &e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
}


来源:https://stackoverflow.com/questions/48575974/boost-socket-example-stuck-in-while-loop

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!