我在使用 Boost::ASIO 解析多个地址时遇到一些问题。我可以调用 async_resolve 一次,获取端点,然后毫无问题地连接到它。但是,如果我想稍后再次调用 async_resolve,async_resolve 将永远挂起并且永远不会调用我的事件处理程序。
有没有办法完成或者关闭之前的async_resolve?还是别的什么?
hostResolver = new boost::asio::ip::tcp::resolver(*internalEventPump->getIoService());
hostResolver->async_resolve(boost::asio::ip::tcp::v4(), hostName, portString,
boost::bind(&DTcpSocket::resolveFinished, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator));
...
void DTcpSocket::resolveFinished(const boost::system::error_code &errorCode,
boost::asio::ip::tcp::resolver::iterator iterator)
{
endPointIter = iterator;
if (errorCode)
{
errMsg = "Couldn't resolve hostname: " + errorCode.message();
connectionHandler(errMsg, DA_HOSTNAME_ERROR);
}
else
{
boost::asio::ip::tcp::endpoint endpoint = *endPointIter;
cout << endpoint.address() << endl;
endPointIter++;//we'll try to connect to this endpoint if the first endpoint failed.
clientSocket->async_connect(endpoint, boost::bind(&DTcpSocket::connectFinished, this,
boost::asio::placeholders::error));
}
}//resolveFinished
根据 sehe 的评论,这确实是一个可以用 work_guard 解决的问题。演示该问题的完整示例,并在评论中修复:
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <chrono>
#include <iostream>
#include <thread>
using namespace std;
static boost::asio::ip::tcp::resolver* HostResolver = nullptr;
static boost::asio::ip::tcp::resolver::iterator EndPointIter = boost::asio::ip::tcp::resolver::iterator();
static boost::asio::ip::tcp::socket* ClientSocket = nullptr;
static bool ConnectInProgress = false;
static const int MAX_ASIO_EVENTS = 1000;
static const unsigned int BUFFER_SIZE = 16384;
static unsigned char* ReceiveBuffer;
void StartConnect();
void ResolveFinished(const boost::system::error_code &errorCode, boost::asio::ip::tcp::resolver::iterator iterator);
void ConnectionFinished(const boost::system::error_code &errorCode);
void ConnectFinished(const boost::system::error_code &errorCode);
void ReceiveFinished(const boost::system::error_code &errorCode, size_t bytesReceived);
bool Close();
bool CurrentlyConnected();
int main(int argc,
char* argv[])
{
ReceiveBuffer = new unsigned char[BUFFER_SIZE];
boost::asio::io_service* ioService = new boost::asio::io_service;
ClientSocket = new boost::asio::ip::tcp::socket(*ioService);
HostResolver = new boost::asio::ip::tcp::resolver(*ioService);
//The fix:
//boost::asio::executor_work_guard<decltype(ioService->get_executor())> work{ioService->get_executor()};
StartConnect();
while (true)
{
int eventsRun = 0;
try
{
while (ioService->poll_one() > 0 && eventsRun < MAX_ASIO_EVENTS)
eventsRun++;
}
catch (exception &e)
{
cerr << "ASIO error " << e.what() << endl;
return 1;
}
if (!ConnectInProgress)
{
if (!CurrentlyConnected())
StartConnect();
}
if (eventsRun == 0)
this_thread::sleep_for(chrono::milliseconds(1));
}
delete ClientSocket;
delete HostResolver;
delete ioService;
delete[] ReceiveBuffer;
return 0;
}//main
bool CurrentlyConnected()
{
boost::system::error_code errorCode;//error code returned by boost
if (ClientSocket->is_open())
{
ClientSocket->remote_endpoint(errorCode);
if (!errorCode)
return true;
}
return false;
}//CurrentlyConnected
void StartConnect()
{
this_thread::sleep_for(chrono::milliseconds(2000));
ConnectInProgress = true;
cout << "Running async_resolve..." << endl;
HostResolver->async_resolve(boost::asio::ip::tcp::v4(), "127.0.0.1", "5127", &ResolveFinished);
}//StartConnect
void ResolveFinished(const boost::system::error_code &errorCode,
boost::asio::ip::tcp::resolver::iterator iterator)
{
cout << "ResolveFinished() called" << endl;
EndPointIter = iterator;
if (errorCode)
{
cout << "Couldn't resolve hostname: " << errorCode.message() << endl;
ConnectInProgress = false;
}
else
{
boost::asio::ip::tcp::endpoint endpoint = *EndPointIter;
EndPointIter++;
cout << "Running async_connect to " << endpoint.address() << endl;
ClientSocket->async_connect(endpoint, &ConnectFinished);
}
}//ResolveFinished
void ConnectFinished(const boost::system::error_code &errorCode)
{
cout << "ConnectFinished() called" << endl;
boost::asio::ip::tcp::endpoint endpoint;//the IP address and port number to connect to
//### Try a different endpoint if we couldn't connect ###
if (errorCode)
{
if (errorCode != boost::asio::error::operation_aborted)
{
//Verify that there's another endpoint
if (EndPointIter == boost::asio::ip::tcp::resolver::iterator())
{
cout << "Couldn't connect: " + errorCode.message() << endl;
ConnectInProgress = false;
return;
}
//Close the socket
if (!Close())
{
ConnectInProgress = false;
return;
}
//Start another asynchronous connect
endpoint = *EndPointIter;
EndPointIter++;
cout << "Running async_connect to " << endpoint.address() << endl;
ClientSocket->async_connect(endpoint, &ConnectFinished);
}
}
//### We connected successfully ###
else
{
cout << "We connected!" << endl;
ConnectInProgress = false;
ClientSocket->async_receive(boost::asio::buffer(ReceiveBuffer, BUFFER_SIZE), &ReceiveFinished);
}
}//ConnectFinished
void ReceiveFinished(const boost::system::error_code &errorCode,
size_t bytesReceived)
{
if (errorCode)
{
if (errorCode != boost::asio::error::operation_aborted)
{
cout << errorCode.message() << endl;
Close();
}
}
else
{
if (bytesReceived > 0)
ClientSocket->async_receive(boost::asio::buffer(ReceiveBuffer, BUFFER_SIZE), &ReceiveFinished);
else
{
cout << "Received zero bytes." << endl;
Close();
}
}
}//ReceiveFinished
bool Close()
{
boost::system::error_code errorCode;
cout << "Close() called" << endl;
if (ClientSocket->is_open())
{
ClientSocket->shutdown(boost::asio::ip::tcp::socket::basic_stream_socket::shutdown_both, errorCode);
if (errorCode)
{
cout << "Got an error invoking shutdown()" << endl;
boost::system::error_condition theError = errorCode.default_error_condition();
if (theError.value() != boost::system::errc::not_connected)
{
cout << "Couldn't shutdown socket: " << theError.message() << endl;
return false;
}
}
ClientSocket->close(errorCode);
if (errorCode)
{
boost::system::error_condition theError = errorCode.default_error_condition();
cout << "Couldn't close socket: " << theError.message() << endl;
return false;
}
}
else
cout << "The socket is already closed" << endl;
return true;
}//Close