考虑这个测试程序:
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <functional>
#include <iostream>
static void callback (boost::asio::ip::tcp::socket && socket)
{
//boost::asio::ip::tcp::socket new_socket = std::move(socket);
std::cout << "Accepted" << std::endl;
}
static void on_accept (boost::asio::ip::tcp::acceptor & acceptor,
boost::asio::ip::tcp::socket & socket,
boost::system::error_code const & error)
{
if (error)
{
std::cerr << error << ' ' << error.message() << std::endl;
return ;
}
callback(std::move(socket));
acceptor.async_accept
(
socket,
std::bind(on_accept, std::ref(acceptor), std::ref(socket), std::placeholders::_1)
);
}
int main ()
{
boost::asio::io_service service;
boost::asio::io_service::work work { service };
boost::asio::ip::tcp::acceptor acceptor { service };
boost::asio::ip::tcp::socket socket { service };
boost::asio::ip::tcp::endpoint endpoint { boost::asio::ip::tcp::v4(), 5555 };
boost::system::error_code ec;
using socket_base = boost::asio::socket_base;
auto option = socket_base::reuse_address { false };
if (acceptor.open(endpoint.protocol(), ec) ||
acceptor.set_option(option, ec) ||
acceptor.bind(endpoint, ec) ||
acceptor.listen(socket_base::max_connections, ec) ||
acceptor.is_open() == false)
return 1;
acceptor.async_accept
(
socket,
std::bind(on_accept, std::ref(acceptor), std::ref(socket), std::placeholders::_1)
);
service.run();
}
当我将客户端连接到它时,我收到错误:
接受
system:1功能不正确
(当on_accept()
函数中的socket
对象被销毁时,将使用错误代码调用callback()
函数。
此外,客户端根本没有断开连接。
如果我取消注释callback()
函数中的行,一切正常,没有错误消息,客户端按预期断开连接。
现在对于环境设置,我在Windows 8.1下,使用MinGW-w64 v4.9.2编译器和Boost.Asio v1.58.0使用相同的编译器编译。
用于编译文件的命令行如下:
$ g++ -std=c++14 -IC:/C++/boost/1.58.0 main.cpp -LC:/C++/boost/1.58.0/lib -lboost_system-mgw49-mt-1_58 -lwsock32 -lws2_32 -o test.exe
请注意,使用Boost 1.57.0会导致相同的行为。
我也可以完全删除注释行,然后使用:
static void callback (boost::asio::ip::tcp::socket && socket)
{
std::cout << "Accepted" << std::endl;
socket.shutdown(socket.shutdown_both);
socket.close();
}
程序也会正常运行。
那么,为什么我需要添加额外的步骤来避免错误? IIRC当我第一次尝试该程序时,这种行为并不存在。
答案 0 :(得分:2)
代码只创建一个套接字,这是一个自动变量,其生命周期将在main()
返回后结束。 std::move(socket)
仅返回可以提供给套接字move constructor的xvalue;它不构造套接字。
要解决此问题,请考虑将callback()
签名更改为通过值接受套接字,允许编译器在给定xvalue时为您调用move-constructor。变化:
static void callback (boost::asio::ip::tcp::socket && socket)
为:
static void callback (boost::asio::ip::tcp::socket socket)
总的来说,代码流程如下:
void callback(socket&&); // rvalue reference.
void on_accept(acceptor&, socket&, ...) // lvalue reference.
{
...
callback(static_cast<socket&&>(socket)); // Cast to xvalue.
acceptor.async_accept(socket,
std::bind(&on_accept, std::ref:acceptor),
std::ref(socket), // lvalue reference.
...);
}
int main()
{
boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
boost::asio::ip::tcp::acceptor acceptor(io_service);
boost::asio::ip::tcp::socket socket(io_service); // Constructor.
...
acceptor.async_accept(socket,
std::bind(&on_accept, std::ref:acceptor),
std::ref(socket), // lvalue reference.
...);
io_service.run();
}
成功接受第一个连接后,main()
中的套接字已打开。 on_accept()
函数使用xvalue调用callback()
,并且不会更改套接字的状态。使用已打开的套接字启动另一个async_accept()
操作,立即导致操作失败。 async_accept()
操作失败,调用将提前返回的on_accept()
,停止其调用链。由于io_service::work
附加到io_service
,执行永远不会从io_service::run()
返回,从而阻止main()
返回并销毁套接字。最终结果是不再接受连接(没有async_accept()
操作)并且客户端未断开连接(socket
永远不会被销毁)。
当callback()
更改套接字的状态以关闭时,问题不再存在,因为满足async_accept()
的前提条件。其他例子符合这一先决条件,因为:
socket(io_service&)
构造函数构造的状态相同的状态。socket.close()
明确关闭。