显示的代码对字符串没有任何作用。此外,您没有显示 io_context 在 Client 实例中使用的内容。
正如给定的,一切都是一个巨大的竞争条件,因为客户端工作可能永远不会运行,但是你总是在发布@的(假定的)异步操作后立即设置done = true 987654326@。
(如果Client::start() 实际上是完全同步的,那么这里可能合理的解释是,但这真的会使static Client& Client::create(...) 的整个存在变得非常奇怪和无用?)。
使用线程休眠是一种反模式,在异步代码中更是如此。
无法取消引用字符串迭代器,因为字符串迭代器已失效
这清楚地表明 MSVC 的 Iterator Debugging 正在工作。它只是告诉你你有一个编程错误。
您的错误导致字符串迭代器在不再有效时被使用。我看不到它,但 99% 的情况下,这是由使用缓冲区的异步操作引起的,该缓冲区在异步操作完成之前被破坏(或修改)。简而言之:
void foo() {
std::string msg = "message";
boost::asio::async_write(_socket, boost::asio::buffer(msg), /*...*/);
}
建议代码
从你的代码中简化,并给出一些提示:
Live On Coliru
#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)
注意有一场启动竞赛。根据您的运气,第一个Client 可能会在Server 开始监听之前尝试连接。我假设这不是您最担心的问题,我将把它留给读者作为练习。