我试图从我的项目的boost TCP客户端示例中创建一个客户端类,并且我注意到有时在连接到不存在的主机时不会调用handle_connect。
我已经在堆栈上阅读了类似的问题,人们忘记在发布任务之前忘记运行io_service或调用它,但我不认为这是我的情况,因为我启动了io_service .run()线程在调用async_connect之后,成功连接,网络无法访问,以及其他一些我测试过的工作正常。
以下是完整列表:
tcp_client.hpp
#ifndef TCP_CLIENT_HPP
#define TCP_CLIENT_HPP
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <mutex>
#include <iostream>
#include <iomanip>
namespace com {
using boost::asio::ip::tcp;
using namespace std;
class client : public boost::enable_shared_from_this<client> {
private:
std::mutex mx_;
bool stopped_ = 1;
boost::asio::streambuf ibuf_;
boost::shared_ptr<boost::asio::io_service> io_service_;
boost::shared_ptr<boost::asio::ip::tcp::socket> sock_;
boost::shared_ptr<tcp::resolver::iterator> ei_;
std::vector<std::string> inbound_;
std::string host_, port_;
public:
client() {}
void connect( std::string host, std::string port ) {
if (!stopped_) stop();
host_ = host; port_ = port;
io_service_.reset(new boost::asio::io_service);
sock_.reset(new boost::asio::ip::tcp::socket(*io_service_));
ei_.reset(new tcp::resolver::iterator);
tcp::resolver r(*io_service_);
ei_ = boost::make_shared<tcp::resolver::iterator>( r.resolve(tcp::resolver::query(host_, port_)) );
stopped_ = 0;
start_connect();
boost::thread work( boost::bind(&client::work, shared_from_this()) );
return;
}
bool is_running() {
return !stopped_;
}
void stop() {
stopped_ = 1;
sock_->close();
return;
}
void send(std::string str) {
if (stopped_) return;
auto msg = boost::asio::buffer(str, str.size());
boost::asio::async_write( (*sock_), msg, boost::bind(&client::handle_write, shared_from_this(), _1) );
return;
}
std::string pull() {
std::lock_guard<std::mutex> lock(mx_);
std::string msg;
if (inbound_.size()>0) {
msg = inbound_.at(0);
inbound_.erase(inbound_.begin());
}
return msg;
}
int size() {
std::lock_guard<std::mutex> lock(mx_);
return inbound_.size();
}
void clear() {
std::lock_guard<std::mutex> lock(mx_);
inbound_.clear();
return;
}
private:
void work() {
if (stopped_) return;
std::cout<<"work in"<<std::endl;
io_service_->run();
std::cout<<"work out"<<std::endl;
return;
}
void start_connect() {
if ((*ei_) != tcp::resolver::iterator()) {
std::cout<<"Trying "<<(*ei_)->endpoint()<<std::endl;
sock_->async_connect( (*ei_)->endpoint(), boost::bind(&client::handle_connect, shared_from_this(), boost::asio::placeholders::error) );
} else {
stop();
}
return;
}
void handle_connect(const boost::system::error_code& ec) {
if (stopped_) return;
if (!sock_->is_open()) {
std::cout<<"Socket closed"<<std::endl;
(*ei_)++;
start_connect();
} else if (ec) {
std::cout<<"Connect error: "<<ec.message()<<std::endl;
sock_->close();
(*ei_)++;
start_connect();
} else {
std::cout<<"Connected to "<<(*ei_)->endpoint()<<std::endl;
start_read();
}
return;
}
void start_read() {
if (stopped_) return;
boost::asio::async_read_until((*sock_), ibuf_, "", boost::bind(&client::handle_read, shared_from_this(), boost::asio::placeholders::error));
return;
}
void handle_read(const boost::system::error_code& ec) {
std::lock_guard<std::mutex> lock(mx_);
if (stopped_) return;
if (ec) {
std::cout<<"Read error: "<<ec.message()<<std::endl;
stop();
return;
}
std::string line;
std::istream is(&ibuf_);
std::getline(is, line);
if (!line.empty() && inbound_.size()<1000) inbound_.push_back(line);
start_read();
return;
}
private:
void handle_write(const boost::system::error_code& ec) {
if (stopped_) return;
if (ec) {
std::cout<<"Write error: "<<ec.message()<<std::endl;
stop();
return;
}
return;
}
};
};
和 tcp_test.cpp
#include "tcp_client.hpp"
int main(int argc, char* argv[]) {
auto tcp_client = boost::shared_ptr<com::client>(new com::client);
try {
tcp_client->connect("192.168.1.15", "50000");
boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
tcp_client->connect("192.168.1.20", "50000");
} catch (std::exception& e) {
std::cerr<<"Exception: "<<e.what()<<std::endl;
}
int cnt=0;
while (cnt<5) {
std::cout<<cnt<<std::endl;
cnt++;
tcp_client->send("<test>");
boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
}
tcp_client->stop();
while (tcp_client->size()>0) std::cout<<tcp_client->pull()<<std::endl;
return 0;
}
我得到的输出是连接到环回服务器时:
Trying 192.168.1.15:50000
work in
work out
Trying 192.168.1.20:50000
0
work in
Connected to 192.168.1.20:50000
1
2
3
4
work out
<test>
<test>
<test>
<test>
<test>
正如你所看到的,192.168.1.20可以正常工作。 192.168.1.15并不存在,但我预计它会引发某种错误。而是io_service.run()立即返回,就像async_connect从未发布回调任务一样。也许它与端点迭代器相关而不是async_connect?
任何人都可以解释为什么会这样吗?
然后我试图在此代码中找出问题:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/thread.hpp>
boost::asio::io_service io_svc;
boost::asio::ip::tcp::socket sock(io_svc);
boost::asio::ip::tcp::resolver::iterator ei;
void work() {
std::cout<<"work in"<<std::endl;
io_svc.run();
std::cout<<"work out"<<std::endl;
return;
}
void stop() {
sock.close();
return;
}
void start_connect();
void handle_connect(const boost::system::error_code& ec) {
if (!sock.is_open()) {
std::cout<<"Socket closed"<<std::endl;
ei++;
start_connect();
} else if (ec) {
std::cout<<"Connect error: "<<ec.message()<<std::endl;
sock.close();
ei++;
start_connect();
} else {
std::cout<<"Connected to "<<ei->endpoint()<<std::endl;
}
return;
}
void start_connect() {
if (ei != boost::asio::ip::tcp::resolver::iterator()) {
std::cout<<"Trying "<<ei->endpoint()<<std::endl;
sock.async_connect( ei->endpoint(), boost::bind(handle_connect, boost::asio::placeholders::error) );
} else {
stop();
}
return;
}
int main(int argc, char* argv[]) {
std::string host="192.168.1.15", port="50000";
boost::asio::ip::tcp::resolver r(io_svc);
ei = r.resolve(boost::asio::ip::tcp::resolver::query(host, port));
start_connect();
boost::thread* thr = new boost::thread(work);
boost::this_thread::sleep_for(boost::chrono::milliseconds(2000));
return 0;
}
但是我得到了完全不同的结果。当我尝试连接到不存在的主机时,大部分时间它都是:
Trying 192.168.1.15:50000
work in
有时它是:
Trying 192.168.1.15:50000
work in
Connect error: Operation canceled
Connect error: Operation canceled
很少发生:
Trying 192.168.1.15:50000
work in
Segmentation fault
&#34;锻炼&#34;从来没有打印过,所以我猜这个例子中的io_service正在做一些事情,但是这与以前的代码有什么不同,以及为什么我得到&#34;操作被取消&#34;有时只出错?
答案 0 :(得分:0)
在后台线程中运行的客户端看起来应该是这样的。
请注意,我注意到包括连接超时等内容。为此,您需要一个与async_connect并行运行的截止时间计时器。然后你必须正确处理交叉情况(提示:在成功连接时取消截止时间计时器并从其async_wait中丢弃随后的错误)。
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/chrono.hpp>
#include <thread>
#include <functional>
boost::asio::io_service io_svc;
struct client
: std::enable_shared_from_this<client>
{
using protocol = boost::asio::ip::tcp;
using resolver = protocol::resolver;
using socket = protocol::socket;
using error_code = boost::system::error_code;
client(boost::asio::io_service& ios)
: ios_(ios) {}
void start(std::string const& host, std::string const& service)
{
auto presolver = std::make_shared<resolver>(get_io_service());
presolver->async_resolve(protocol::resolver::query(host, service),
strand_.wrap([self = shared_from_this(), presolver](auto&& ec, auto iter)
{
self->handle_resolve(ec, presolver, iter);
}));
}
private:
void
handle_resolve(boost::system::error_code const& ec, std::shared_ptr<resolver> presolver, resolver::iterator iter)
{
if (ec) {
std::cerr << "error resolving: " << ec.message() << std::endl;
}
else {
boost::asio::async_connect(sock, iter, strand_.wrap([self = shared_from_this(),
presolver]
(auto&& ec, auto iter)
{
self->handle_connect(ec, iter);
// note - we're dropping presolver here - we don't need it any more
}));
}
}
void handle_connect(error_code const& ec, resolver::iterator iter)
{
if (ec) {
std::cerr << "failed to connect: " << ec.message() << std::endl;
}
else {
auto payload = std::make_shared<std::string>("Hello");
boost::asio::async_write(sock, boost::asio::buffer(*payload),
strand_.wrap([self = shared_from_this(),
payload] // note! capture the payload so it continues to exist during async send
(auto&& ec, auto size)
{
self->handle_send(ec, size);
}));
}
}
void handle_send(error_code const& ec, std::size_t size)
{
if (ec) {
std::cerr << "send failed after " << size << " butes : " << ec.message() << std::endl;
}
else {
// send something else?
}
}
boost::asio::io_service& get_io_service()
{
return ios_;
}
private:
boost::asio::io_service& ios_;
boost::asio::strand strand_{get_io_service()};
socket sock{get_io_service()};
};
void work()
{
std::cout << "work in" << std::endl;
io_svc.run();
std::cout << "work out" << std::endl;
return;
}
int main(int argc, char *argv[])
{
auto pclient = std::make_shared<client>(io_svc);
std::string host = "192.168.1.15", port = "50000";
pclient->start(host, port);
auto run_thread = std::thread(work);
if (run_thread.joinable())
run_thread.join();
return 0;
}
示例输出:
work in
<time passes>...
failed to connect: Operation timed out
work out