我正在编写一个带有Boost的UDP服务器应用,应该在套接字上监听5秒钟,如果在这5秒内没有收到数据报,继续做其他事情。
受some answers的启发,我决定尝试基于std :: future的解决方案。
问题是对wait_for()
的调用总是超时,好像没有收到数据一样。但是如果我在超时后执行的行上设置断点并且我检查变量,我会看到缓冲区包含接收的数据报,remote_endpoint
对象包含客户端的地址。换句话说,套接字接收按预期工作,但std :: future不会触发。为什么呢?
这是我的测试服务器代码:
#include <future>
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>
using boost::asio::ip::udp;
int main()
{
try
{
boost::asio::io_service io_service;
udp::socket socket(io_service, udp::endpoint(udp::v4(), 10000));
char recv_buf[8];
for (;;)
{
ZeroMemory(recv_buf, 8);
udp::endpoint remote_endpoint;
std::future<std::size_t> recv_length;
recv_length = socket.async_receive_from(
boost::asio::buffer(recv_buf),
remote_endpoint,
0,
boost::asio::use_future);
if (recv_length.wait_for(
std::chrono::seconds(5)) == std::future_status::timeout)
{
printf("time out. Nothing received.\n");
}
else
{
printf("received something: %s\n", recv_buf);
}
}
}
catch (std::exception& e)
{
printf("Error: %s\n", e.what());
}
return 0;
}
我一直在敲打这个问题,所以任何帮助都会受到赞赏。我在使用Visual Studio 2015的Windows 10上。
这是我的测试客户端代码(在python中,抱歉)。
import socket
import time
HOST = "server" # The remote host
PORT = 10000 # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
address = socket.getaddrinfo(HOST, PORT)[0][-1]
while True:
s.sendto("ping\0", address)
time.sleep(1)
答案 0 :(得分:3)
您没有调用import FSCalendar
对象的io_service
方法。因此asio没有运行。请创建一个调用run
方法的线程,然后重试。
答案 1 :(得分:0)
您正在做的是混合使用异步和同步操作,但这些操作无效:
async_receive_from
操作,该操作将在asio事件循环(io_service
)上运行,并在收到任何内容时完成。在正常情况下,这将在完成时调用回调,如果你给它未来它将完成未来。请注意,这将在调用io_service.run()
解决此问题的可能步骤:
then()
方法以附加延续而不是阻止。不适用于旧的stdlib期货,因为它是C ++ 17扩展。然而,提振期货可以做到。您仍然需要在主线程上调用io_service.run()并将程序拆分为回调之前和之后的阶段。答案 2 :(得分:0)
对于异步操作,底层I / O和完成处理程序的执行是不连续的步骤。在这种情况下,I / O已完成,但用户代码从不运行io_service
,因此永远不会执行设置recv_length
值的完成处理程序。要解决此问题,请运行io_service
。
有一些细节有助于观察:
io_service
as-if {{3} } boost::asio::use_future
时,std::future
的值在异步操作的完成处理程序中设置io_service
的poll()
,poll_one()
,run()
和run_one()
成员函数的线程中调用io_service
在问题的背景下,何时
recv_length = socket.async_receive_from(
boost::asio::buffer(recv_buf),
remote_endpoint,
0,
boost::asio::use_future);
已启动且数据可供阅读(socket.available() > 0
),然后remote_endpoint
和recv_buffer
将在初始async_receive_from()
功能中填充正确的数据。设置recv_length
值的完成处理程序将发布到io_service
。但是,由于代码不处理io_service
,因此永远不会设置recv_length
的值。因此,recv_length.wait_for()
将始终导致超时状态。
io_service.post()
创建了一个额外的线程,专门用于处理I / O服务,并在未处理I / O服务的线程内等待std::future
:
// We run the io_service off in its own thread so that it operates
// completely asynchronously with respect to the rest of the program.
boost::asio::io_service io_service;
boost::asio::io_service::work work(io_service);
std::thread thread([&io_service](){ io_service.run(); });
...
std::future<std::size_t> send_length =
socket.async_send_to(..., boost::asio::use_future);
// Do other things here while the send completes.
send_length.get(); // Blocks until the send is complete. Throws any errors.
io_service.stop();
thread.join();
答案 3 :(得分:0)
我找到了解决方案。所以要把它包起来,这是需要做的。我的初始代码需要2次修改。
(1)在开头添加2行,用io_service启动一个单独的线程来监控超时(正如Tanner Sansbury所建议的那样)
<add name="ConnectionString_LIVE_customer_support" connectionString="server=REMOVED;port=REMOVED;User Id=REMOVED;password=REMOVED;Persist Security Info=True;database=customer_support;Sql Server Mode=True;Allow User Variables=True" providerName="MySql.Data.MySqlClient" />
(2)在socket time_out的条件下调用boost::asio::io_service::work work(io_service);
std::thread thread([&io_service](){ io_service.run(); });
。如果未取消套接字操作,则尽管重新调用socket.cancel();
(在Boost&#39的邮件列表中收到解决方案),套接字仍将保持阻塞状态。
以下是修订后的参考代码:
wait_for()
谢谢大家的帮助。