我的客户端应用程序使用boost::asio::ip::tcp::socket
连接到远程服务器。如果应用程序失去与此服务器的连接(例如,由于服务器崩溃或关闭),我希望它尝试定期重新连接,直到成功。
我需要在客户端做些什么才能干净利落地处理断开连接、整理并重复尝试重新连接?
目前,我的代码中有趣的部分看起来像这样。
I connect
就像这样:
bool MyClient::myconnect()
{
bool isConnected = false;
// Attempt connection
socket.connect(server_endpoint, errorcode);
if (errorcode)
{
cerr << "Connection failed: " << errorcode.message() << endl;
mydisconnect();
}
else
{
isConnected = true;
// Connected so setup async read for an incoming message.
startReadMessage();
// And start the io_service_thread
io_service_thread = new boost::thread(
boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
}
return (isConnected)
}
其中runIOServer()
方法只是:
void MyClient::runIOService(boost::asio::io_service& io_service)
{
size_t executedCount = io_service.run();
cout << "io_service: " << executedCount << " handlers executed." << endl;
io_service.reset();
}
如果任何异步读取处理程序返回错误,则它们只需调用此disconnect
方法:
void MyClient::mydisconnect(void)
{
boost::system::error_code errorcode;
if (socket.is_open())
{
// Boost documentation recommends calling shutdown first
// for "graceful" closing of socket.
socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
if (errorcode)
{
cerr << "socket.shutdown error: " << errorcode.message() << endl;
}
socket.close(errorcode);
if (errorcode)
{
cerr << "socket.close error: " << errorcode.message() << endl;
}
// Notify the observer we have disconnected
myObserver->disconnected();
}
..which尝试正常断开连接,然后通知观察者,观察者将以五秒的间隔开始调用connect()
,直到重新连接为止。
还需要我做什么吗?
目前,这似乎确实有效。如果我杀死它所连接的服务器,我会在我的读处理程序中得到预期的"End of file"
错误,并且mydisconnect()
被调用时没有任何问题。
但是当它尝试重新连接并失败时,我看到它报告"socket.shutdown error: Invalid argument"
。这仅仅是因为我试图关闭一个没有读/写挂起的套接字吗?或者是更多的东西?
发布于 2010-06-19 06:06:27
每次重新连接时都需要创建一个新的boost::asio::ip::tcp::socket
。要做到这一点,最简单的方法可能是使用boost::shared_ptr
在堆上分配套接字(如果套接字完全封装在类中,也可以使用scoped_ptr
)。例如:
bool MyClient::myconnect()
{
bool isConnected = false;
// Attempt connection
// socket is of type boost::shared_ptr<boost::asio::ip::tcp::socket>
socket.reset(new boost::asio::ip::tcp::socket(...));
socket->connect(server_endpoint, errorcode);
// ...
}
然后,当调用mydisconnect
时,您可以释放套接字:
void MyClient::mydisconnect(void)
{
// ...
// deallocate socket. will close any open descriptors
socket.reset();
}
您看到的错误可能是操作系统在您调用close
之后清除了文件描述符的结果。当您调用close
,然后尝试在同一套接字上执行connect
时,您可能正在尝试连接无效的文件描述符。此时,您应该看到一条以"Connection failed:...“开头的错误消息。基于您的逻辑,但是您随后调用了mydisconnect
,而后者随后可能试图在无效的文件描述符上调用shutdown
。恶性循环!
发布于 2010-06-25 19:49:45
为了清楚起见,这里是我使用的最后一种方法(但这是基于bjlaub的回答,所以请对他表示赞赏):
我将socket
成员声明为scoped_ptr
:
boost::scoped_ptr<boost::asio::ip::tcp::socket> socket;
然后我将我的connect
方法修改为:
bool MyClient::myconnect()
{
bool isConnected = false;
// Create new socket (old one is destroyed automatically)
socket.reset(new boost::asio::ip::tcp::socket(io_service));
// Attempt connection
socket->connect(server_endpoint, errorcode);
if (errorcode)
{
cerr << "Connection failed: " << errorcode.message() << endl;
socket->close();
}
else
{
isConnected = true;
// Connected so setup async read for an incoming message.
startReadMessage();
// And start the io_service_thread
io_service_thread = new boost::thread(
boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
}
return (isConnected)
}
注意:这个问题最初是在2010年提出并回答的,但如果您现在使用C++11或更高版本,那么std::unique_ptr
通常会是比boost::scoped_ptr
更好的选择
发布于 2018-08-16 18:34:44
从C++11开始,你可以这样写:
decltype(socket)(std::move(socket));
// reconnect socket.
上面的代码创建了socket类型move的本地实例,从socket构造它。
在下一行之前,未命名的本地实例被析构,使套接字处于“从io_service新构造的”状态。
https://stackoverflow.com/questions/3062803
复制相似问题