boost::asio 挂起 _endthreadx
boost::asio hangs _endthreadx
int main(){
boost::asio::io_context io_context;
Server server(io_context, SOCKET_ADDRESS, SOCKET_PORT);
std::thread thread_server([&]() {
server.start();
io_context.run();
});
std::thread thread_client([&]() {
Client &client = Client::create(SOCKET_ADDRESS, SOCKET_PORT);
client.start();
done = true; // <-----atomic
});
std::thread thread_stop([&]() {
while (done == false) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
server.stop();
});
thread_server.join();
thread_client.join();
thread_stop.join();
}
我正在试验 boost::asio
,遇到了我无法解决的问题。当我在 Linux(用 gcc 编译)上 运行 编程(上面更简单的例子)时,一切都很好。当我 运行 在 VS2017CE 中发布时也是如此。但是,当我 运行 它在 Debug 上(也包括 VS2017CE)时,它崩溃并出现异常:
cannot dereference string iterator because string iterator was invalidated
它在退出 thread_stop
或 thread_server
时在 _endthreadx
崩溃(很可能是第二个)。下面是我的问题:
Release 和 Debug 基本配置之间有什么区别可能会影响代码执行并指出我应该查看的位置。(我知道一些但找不到与此特定问题相关的任何内容.)
我犯了什么错误影响代码执行
我已经做了一些 类 所以如果需要的话我会提供更多的代码,但是代码基本上可以工作所以我只从其中的一部分开始。
显示的代码不对字符串执行任何操作。此外,您没有显示 io_context
在 Client
实例中使用的内容。
正如给定的那样,一切都是巨大的竞争条件,因为 none 的客户工作可能会得到 运行, 但是 你总是设置 done = true
在发布 Client::start
的(假定的)异步操作后立即执行。
(这里可能的理智解释是,如果 Client::start()
实际上是完全同步的,但这真的会使 static Client& Client::create(...)
的整个存在变得非常奇怪和无用吗?)。
使用线程休眠是一种反模式,在异步代码中更是如此。
cannot dereference string iterator because string iterator was invalidated
这是一个明显的迹象,表明 MSVC 的 Iterator Debugging 正在运行。它只是告诉你你有一个编程错误。
您的错误导致字符串迭代器在不再有效时被使用。我看不到它,但 99% 的情况下,这是由异步操作使用在异步操作完成之前被销毁(或修改)的缓冲区引起的。简而言之:
void foo() {
std::string msg = "message";
boost::asio::async_write(_socket, boost::asio::buffer(msg), /*...*/);
}
建议代码
正在简化您的代码,并显示一些提示:
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
using boost::asio::ip::tcp;
using boost::system::error_code;
static std::string const SOCKET_ADDRESS = "127.0.0.1";
static unsigned short const SOCKET_PORT = 6767;
bool check(error_code const& ec, char const* message) {
std::cout << message << " (" << ec.message() << ")\n";
return !ec;
}
struct Server {
boost::asio::io_context& io_;
Server(boost::asio::io_context& io, std::string host, unsigned short port) : io_(io), host_(host), port_(port) {}
void start() {
acc_.set_option(tcp::acceptor::reuse_address(true));
acc_.listen(5);
accept_loop();
}
void stop() {
io_.post([this] { // thread safety
acc_.cancel();
acc_.close();
});
}
private:
void accept_loop() {
acc_.async_accept(sock_, [this](error_code ec) {
if (check(ec, "accepted")) {
std::make_shared<Connection>(std::move(sock_))->start();
accept_loop();
}
});
}
struct Connection : std::enable_shared_from_this<Connection> {
tcp::socket sock_;
std::string buffer_;
Connection(tcp::socket&& sock) : sock_(std::move(sock)) {}
~Connection() {
error_code ec;
std::cout << "Disconnected " << sock_.remote_endpoint(ec) << "\n";
}
void start() {
auto self = shared_from_this();
async_read_until(sock_, boost::asio::dynamic_buffer(buffer_), "\n", [self,this](error_code ec, size_t bytes) {
if (check(ec, "received request")) {
std::cout << "Request: " << std::quoted(buffer_.substr(0, bytes), '\'') << "\n";
if (bytes > 0)
std::reverse(buffer_.begin(), buffer_.begin() + bytes - 1); // reverse the request for the response
async_write(sock_, boost::asio::buffer(buffer_, bytes), [self,this](error_code ec, size_t bytes) {
if (check(ec, "response sent")) {
buffer_.erase(0, bytes);
start(); // handle more requests, if any
}
});
}
});
}
};
std::string host_;
unsigned short port_;
tcp::acceptor acc_{io_, {boost::asio::ip::address_v4::from_string(host_), port_}};
tcp::socket sock_{io_};
};
struct Client {
Client(std::string host, std::string port) : host_(host), port_(port) {}
void start() {
boost::asio::io_context io;
tcp::socket s(io);
tcp::resolver r(io);
connect(s, r.resolve(host_, port_));
send_request(s, "hello world\n");
send_request(s, "bye world\n");
}
private:
void send_request(tcp::socket& s, std::string const& request) {
write(s, boost::asio::buffer(request));
boost::asio::streambuf sb;
read_until(s, sb, "\n");
std::cout << "Received server response: '" << &sb << "'\n";
}
std::string host_;
std::string port_;
};
int main(){
boost::asio::io_context io_context;
Server server(io_context, SOCKET_ADDRESS, SOCKET_PORT);
server.start();
std::thread thread_server([&]() { io_context.run(); });
{
Client client {SOCKET_ADDRESS, std::to_string(SOCKET_PORT)};
client.start();
}
{
Client client {SOCKET_ADDRESS, std::to_string(SOCKET_PORT)};
client.start();
}
server.stop();
thread_server.join();
}
打印
accepted (Success)
received request (Success)
Request: 'hello world
'
response sent (Success)
Received server response: 'dlrow olleh
'
received request (Success)
Request: 'bye world
'
response sent (Success)
Received server response: 'dlrow eyb
'
received request (End of file)
Disconnected 127.0.0.1:49778
accepted (Success)
received request (Success)
Request: 'hello world
'
response sent (Success)
Received server response: 'dlrow olleh
'
received request (Success)
Request: 'bye world
'
response sent (Success)
Received server response: 'dlrow eyb
'
received request (End of file)
Disconnected 127.0.0.1:49780
accepted (Operation canceled)
Note There's a startup race. Depending on your luck, the first Client
might try to connect before the Server
has started listening. I'm assuming this is not your worst worry, and I'll leave it as an exercise for the reader.
int main(){
boost::asio::io_context io_context;
Server server(io_context, SOCKET_ADDRESS, SOCKET_PORT);
std::thread thread_server([&]() {
server.start();
io_context.run();
});
std::thread thread_client([&]() {
Client &client = Client::create(SOCKET_ADDRESS, SOCKET_PORT);
client.start();
done = true; // <-----atomic
});
std::thread thread_stop([&]() {
while (done == false) {
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
server.stop();
});
thread_server.join();
thread_client.join();
thread_stop.join();
}
我正在试验 boost::asio
,遇到了我无法解决的问题。当我在 Linux(用 gcc 编译)上 运行 编程(上面更简单的例子)时,一切都很好。当我 运行 在 VS2017CE 中发布时也是如此。但是,当我 运行 它在 Debug 上(也包括 VS2017CE)时,它崩溃并出现异常:
cannot dereference string iterator because string iterator was invalidated
它在退出 thread_stop
或 thread_server
时在 _endthreadx
崩溃(很可能是第二个)。下面是我的问题:
Release 和 Debug 基本配置之间有什么区别可能会影响代码执行并指出我应该查看的位置。(我知道一些但找不到与此特定问题相关的任何内容.)
我犯了什么错误影响代码执行
我已经做了一些 类 所以如果需要的话我会提供更多的代码,但是代码基本上可以工作所以我只从其中的一部分开始。
显示的代码不对字符串执行任何操作。此外,您没有显示 io_context
在 Client
实例中使用的内容。
正如给定的那样,一切都是巨大的竞争条件,因为 none 的客户工作可能会得到 运行, 但是 你总是设置 done = true
在发布 Client::start
的(假定的)异步操作后立即执行。
(这里可能的理智解释是,如果 Client::start()
实际上是完全同步的,但这真的会使 static Client& Client::create(...)
的整个存在变得非常奇怪和无用吗?)。
使用线程休眠是一种反模式,在异步代码中更是如此。
cannot dereference string iterator because string iterator was invalidated
这是一个明显的迹象,表明 MSVC 的 Iterator Debugging 正在运行。它只是告诉你你有一个编程错误。
您的错误导致字符串迭代器在不再有效时被使用。我看不到它,但 99% 的情况下,这是由异步操作使用在异步操作完成之前被销毁(或修改)的缓冲区引起的。简而言之:
void foo() {
std::string msg = "message";
boost::asio::async_write(_socket, boost::asio::buffer(msg), /*...*/);
}
建议代码
正在简化您的代码,并显示一些提示:
#include <boost/asio.hpp>
#include <iostream>
#include <iomanip>
using boost::asio::ip::tcp;
using boost::system::error_code;
static std::string const SOCKET_ADDRESS = "127.0.0.1";
static unsigned short const SOCKET_PORT = 6767;
bool check(error_code const& ec, char const* message) {
std::cout << message << " (" << ec.message() << ")\n";
return !ec;
}
struct Server {
boost::asio::io_context& io_;
Server(boost::asio::io_context& io, std::string host, unsigned short port) : io_(io), host_(host), port_(port) {}
void start() {
acc_.set_option(tcp::acceptor::reuse_address(true));
acc_.listen(5);
accept_loop();
}
void stop() {
io_.post([this] { // thread safety
acc_.cancel();
acc_.close();
});
}
private:
void accept_loop() {
acc_.async_accept(sock_, [this](error_code ec) {
if (check(ec, "accepted")) {
std::make_shared<Connection>(std::move(sock_))->start();
accept_loop();
}
});
}
struct Connection : std::enable_shared_from_this<Connection> {
tcp::socket sock_;
std::string buffer_;
Connection(tcp::socket&& sock) : sock_(std::move(sock)) {}
~Connection() {
error_code ec;
std::cout << "Disconnected " << sock_.remote_endpoint(ec) << "\n";
}
void start() {
auto self = shared_from_this();
async_read_until(sock_, boost::asio::dynamic_buffer(buffer_), "\n", [self,this](error_code ec, size_t bytes) {
if (check(ec, "received request")) {
std::cout << "Request: " << std::quoted(buffer_.substr(0, bytes), '\'') << "\n";
if (bytes > 0)
std::reverse(buffer_.begin(), buffer_.begin() + bytes - 1); // reverse the request for the response
async_write(sock_, boost::asio::buffer(buffer_, bytes), [self,this](error_code ec, size_t bytes) {
if (check(ec, "response sent")) {
buffer_.erase(0, bytes);
start(); // handle more requests, if any
}
});
}
});
}
};
std::string host_;
unsigned short port_;
tcp::acceptor acc_{io_, {boost::asio::ip::address_v4::from_string(host_), port_}};
tcp::socket sock_{io_};
};
struct Client {
Client(std::string host, std::string port) : host_(host), port_(port) {}
void start() {
boost::asio::io_context io;
tcp::socket s(io);
tcp::resolver r(io);
connect(s, r.resolve(host_, port_));
send_request(s, "hello world\n");
send_request(s, "bye world\n");
}
private:
void send_request(tcp::socket& s, std::string const& request) {
write(s, boost::asio::buffer(request));
boost::asio::streambuf sb;
read_until(s, sb, "\n");
std::cout << "Received server response: '" << &sb << "'\n";
}
std::string host_;
std::string port_;
};
int main(){
boost::asio::io_context io_context;
Server server(io_context, SOCKET_ADDRESS, SOCKET_PORT);
server.start();
std::thread thread_server([&]() { io_context.run(); });
{
Client client {SOCKET_ADDRESS, std::to_string(SOCKET_PORT)};
client.start();
}
{
Client client {SOCKET_ADDRESS, std::to_string(SOCKET_PORT)};
client.start();
}
server.stop();
thread_server.join();
}
打印
accepted (Success)
received request (Success)
Request: 'hello world
'
response sent (Success)
Received server response: 'dlrow olleh
'
received request (Success)
Request: 'bye world
'
response sent (Success)
Received server response: 'dlrow eyb
'
received request (End of file)
Disconnected 127.0.0.1:49778
accepted (Success)
received request (Success)
Request: 'hello world
'
response sent (Success)
Received server response: 'dlrow olleh
'
received request (Success)
Request: 'bye world
'
response sent (Success)
Received server response: 'dlrow eyb
'
received request (End of file)
Disconnected 127.0.0.1:49780
accepted (Operation canceled)
Note There's a startup race. Depending on your luck, the first
Client
might try to connect before theServer
has started listening. I'm assuming this is not your worst worry, and I'll leave it as an exercise for the reader.