我正在为我的全双工服务器编写测试,当我执行多个(顺序)调用(虽然被一条链覆盖)时,我从文件中async_write
得到以下断言错误boost::beast
boost/beast/websocket/detail/stream_base.hpp
:
// If this assert goes off it means you are attempting to
// simultaneously initiate more than one of same asynchronous
// operation, which is not allowed. For example, you must wait
// for an async_read to complete before performing another
// async_read.
//
BOOST_ASSERT(id_ != T::id);
要在您的机器上重现该问题:可在此处找到重现此问题 (MCVE) 的完整客户端代码。它在链接中不起作用,因为您需要一个服务器(在您自己的机器上,抱歉,因为无法方便地在线执行此操作,这更好地客观地表明问题出在客户端,而不是服务器,如果我把它包括在这里)。我使用websocketd./websocketd --ssl --sslkey /path/to/server.key --sslcert /path/to/server.crt --port=8085 ./prog.py
使用命令where创建服务器./prog.py
一个简单的 python 程序,用于打印和刷新(我从websocketd 主页获得)。
在客户端进行写入的调用如下所示:
std::vector<std::vector<std::future<void>>> clients_write_futures(
clients_count);
for (int i = 0; i < clients_count; i++) {
clients_write_futures[i] = std::vector<std::future<void>>(num_of_messages);
for (int j = 0; j < num_of_messages; j++) {
clients_write_futures[i][j] =
clients[i]->write_data_async_future("Hello"); // writing here
}
}
请注意,我在示例中仅使用了 1 个客户端。客户端数组只是在测试时对服务器施加更多压力的概括。
我对这个问题的评论:
- 循环是顺序的;这不像我在多个线程中这样做
- 应该可以以全双工形式进行通信,其中无限数量的消息被发送到服务器。全双工通讯还能怎么做?
- 我正在使用 strands 来包装我的异步调用,以防止通过 io_service/io_context 在套接字中发生任何冲突
- 使用调试器对此进行调查显示循环的第二次迭代始终失败,这意味着我在做一些根本错误的事情,但我不知道它是什么。换句话说:这显然是一个确定性问题。
我在这里做错了什么?如何向我的 websocket 服务器写入不定数量的消息?
编辑:
Sehe,我想首先为代码混乱道歉(没有意识到这很糟糕),并感谢您为此付出的努力。我希望你问我为什么它同时以这种(可能)有组织且混乱的方式构建,答案很简单:主要是一个 gtest 代码,用于查看我用来强调的通用、多功能 websocket 客户端是否工作-测试我的服务器(它使用大量多线程 io_service 对象,我认为这些对象很敏感,需要进行广泛的测试)。我计划在实际生产测试期间同时用许多客户端轰炸我的服务器。我发布这个问题是因为我不理解客户的行为。我在这个文件中所做的是创建一个 MCVE(人们一直要求 SO)。我花了两个小时剥离我的代码来创建它,
那么为什么我不捕捉异常呢?因为 gtest 会捕获它们并认为测试失败。主要不是生产代码,而是客户端。我从你提到的东西中学到了很多,我不得不说扔和接是愚蠢的,但我不知道 std::make_exception_ptr(),所以我找到了我的 (dumm) 方法来实现相同的结果:- )。为什么有太多无用的功能:在这个测试/示例中它们在这里没用,但通常我可以稍后将它们用于其他事情,因为这个客户端不仅适用于这种情况。
现在回到问题:我不明白的是,为什么我们必须在async_write
strand_ 在主线程的循环中顺序使用它时覆盖它(我错误地表达了我只覆盖了处理程序)。我会理解为什么要覆盖处理程序,因为套接字不是线程安全的,并且多线程io_service
会在那里产生竞争。我们也知道它io_service::post
本身是线程安全的(这就是我认为不需要包装 async_write 的原因)。你能解释一下在我们需要包装 async_write 本身时,什么不是线程安全的吗?我知道你已经知道这一点,但同样的断言仍在触发。我们对处理程序和异步队列进行了顺序化,客户端仍然不喜欢进行多次写入调用。还能缺少什么?
(顺便说一句,如果你写,然后得到未来,然后读,然后再写,它会起作用。这就是为什么我使用期货来准确定义测试用例并定义我的测试的时间顺序。我很偏执这里。)