boost::asio::ip::udp::socket::send_to 甚至会失败吗?
May boost::asio::ip::udp::socket::send_to even fail?
请考虑以下代码片段。
它首先解析远程主机的地址,然后打开套接字并向其发送一些数据。注意,它会在发生错误时立即抛出。
不涉及并发。消息适合 1K。基本上,此代码片段与“真实”代码之间的唯一区别如下:消息可能会在解析端点并打开套接字后的几秒钟内发送。
using namespace boost::asio;
io_context io_context;
ip::udp::resolver resolver{io_context};
const auto endpoints = resolver.resolve(ip::udp::v4(), "host", "port");
if (endpoints.empty())
throw std::runtime_error("No endpoints found");
const auto endpoint = endpoints->endpoint();
ip::udp::socket socket{io_context};
socket.open(ip::udp::v4());
const auto message = buffer("asdf"); // fits to 1K
// may the line below fail provided the code above is executed successfully?
socket.send_to(message, endpoint);
对我来说,只要端点有效且套接字打开成功,似乎对 socket.send_to
的调用应该总是成功的,即使远程主机变得不可用(因为使用了 UDP)。
- 我应该在最后一行期待什么异常?
- 我可以假设不会出现任何错误吗?
- 我是否应该期待 任何 IO 相关的错误代码,因为我们无论如何都在做 IO?
ASIO 在底层操作系统调用方面实现了这一点。在 POSIX 上,那将是 sendto
。记录了可能的错误情况(见下文)。
但是,首先要做的是:
您可能会收到一个段错误,因为地址越界进入未知地址 space。根据您的平台,它可能显示为 EFAULT
(boost::asio::error::fault
).
const_buffer buffer{"asdf", 10};
这里的首选拼写是:
auto buffer = boost::asio::buffer("asdf"); // char[5] includes NUL
将发送 char[]
(包括终止 NUL 字符)(参见 overload)。如果您不想要那样,请考虑例如boost::asio::buffer("asdf"sv)
,它使用字符串视图,无需调用 strlen
.
Note how you create naming conflicts where buffer
hides boost::asio::buffer
because of using namespace
. You did the same with io_context
. I'd advise against this level of flirting with danger in C++
其他注意事项
if (ec)
throw std::system_error(ec);
不需要。如果您不提供 ec
,异常 boost::system::system_error
(但来自 boost)将以相同的方式引发。
size_t sent = socket.send_to(
ba::buffer("asdf"),
endpoints->endpoint());
您在未验证解析器结果的情况下使用 endpoints->endpoint()
。根据情况,可能存在零个或多个解决方案。您可能正在取消引用无效的迭代器。这又会导致错误情况。
其他错误代码
您可以从 POSIX 文档中获得灵感:https://pubs.opengroup.org/onlinepubs/009695399/functions/sendto.html
绝大多数条件不适用,部分原因在于
- 是数据报,不是流协议
- 此处套接字未处于非阻塞模式(或被 ASIO 抽象掉)
- 套接字“已知良好”(假设没有您未显示的并发代码)
但是还有一些:
EACCESS
如果您像使用多播一样使用常规端点,则可能会发生
EDESTADDRREQ
如果你传递了一个无效的端点(例如默认构造的)
EINTR
除非你忽略了信号
ENOBUFS
(当适配器卡住时 - 在 linux 上不会发生数据包被丢弃的情况)
取决于您实际代码中的实际参数:
EMSGSIZE
如果您的缓冲区超出了可以自动发送的限制
EOPNOTSUPP
如果你传递了无效的标志
摘要
真正的问题是:您是否预料到任何应该处理的错误?如果不是简单地接受异常(我建议不要传递error_code
参数)。
我能想到的唯一一种情况是无法解析主机名。但是,快速测试告诉我结果集不会为空,而是 resolve
抛出 Host not found (authoritative)
.
所以,简化一下:
#include <boost/asio.hpp>
using namespace std::literals;
namespace ba = boost::asio;
using ba::ip::udp;
int main() {
ba::io_context io;
udp::resolver resolver{io};
auto endpoints = resolver.resolve(udp::v4(), "127.0.0.1", "6767");
udp::socket socket{io};
socket.open(udp::v4());
return socket.send_to(ba::buffer("asdf"sv), endpoints->endpoint());
}
有
nc -u -l -p 6767 & sleep 1; ./a.out
版画
asdf
请考虑以下代码片段。
它首先解析远程主机的地址,然后打开套接字并向其发送一些数据。注意,它会在发生错误时立即抛出。
不涉及并发。消息适合 1K。基本上,此代码片段与“真实”代码之间的唯一区别如下:消息可能会在解析端点并打开套接字后的几秒钟内发送。
using namespace boost::asio;
io_context io_context;
ip::udp::resolver resolver{io_context};
const auto endpoints = resolver.resolve(ip::udp::v4(), "host", "port");
if (endpoints.empty())
throw std::runtime_error("No endpoints found");
const auto endpoint = endpoints->endpoint();
ip::udp::socket socket{io_context};
socket.open(ip::udp::v4());
const auto message = buffer("asdf"); // fits to 1K
// may the line below fail provided the code above is executed successfully?
socket.send_to(message, endpoint);
对我来说,只要端点有效且套接字打开成功,似乎对 socket.send_to
的调用应该总是成功的,即使远程主机变得不可用(因为使用了 UDP)。
- 我应该在最后一行期待什么异常?
- 我可以假设不会出现任何错误吗?
- 我是否应该期待 任何 IO 相关的错误代码,因为我们无论如何都在做 IO?
ASIO 在底层操作系统调用方面实现了这一点。在 POSIX 上,那将是 sendto
。记录了可能的错误情况(见下文)。
但是,首先要做的是:
您可能会收到一个段错误,因为地址越界进入未知地址 space。根据您的平台,它可能显示为 EFAULT
(boost::asio::error::fault
).
const_buffer buffer{"asdf", 10};
这里的首选拼写是:
auto buffer = boost::asio::buffer("asdf"); // char[5] includes NUL
将发送 char[]
(包括终止 NUL 字符)(参见 overload)。如果您不想要那样,请考虑例如boost::asio::buffer("asdf"sv)
,它使用字符串视图,无需调用 strlen
.
Note how you create naming conflicts where
buffer
hidesboost::asio::buffer
because ofusing namespace
. You did the same withio_context
. I'd advise against this level of flirting with danger in C++
其他注意事项
if (ec)
throw std::system_error(ec);
不需要。如果您不提供 ec
,异常 boost::system::system_error
(但来自 boost)将以相同的方式引发。
size_t sent = socket.send_to(
ba::buffer("asdf"),
endpoints->endpoint());
您在未验证解析器结果的情况下使用 endpoints->endpoint()
。根据情况,可能存在零个或多个解决方案。您可能正在取消引用无效的迭代器。这又会导致错误情况。
其他错误代码
您可以从 POSIX 文档中获得灵感:https://pubs.opengroup.org/onlinepubs/009695399/functions/sendto.html
绝大多数条件不适用,部分原因在于
- 是数据报,不是流协议
- 此处套接字未处于非阻塞模式(或被 ASIO 抽象掉)
- 套接字“已知良好”(假设没有您未显示的并发代码)
但是还有一些:
EACCESS
如果您像使用多播一样使用常规端点,则可能会发生EDESTADDRREQ
如果你传递了一个无效的端点(例如默认构造的)EINTR
除非你忽略了信号ENOBUFS
(当适配器卡住时 - 在 linux 上不会发生数据包被丢弃的情况)
取决于您实际代码中的实际参数:
EMSGSIZE
如果您的缓冲区超出了可以自动发送的限制EOPNOTSUPP
如果你传递了无效的标志
摘要
真正的问题是:您是否预料到任何应该处理的错误?如果不是简单地接受异常(我建议不要传递error_code
参数)。
我能想到的唯一一种情况是无法解析主机名。但是,快速测试告诉我结果集不会为空,而是 resolve
抛出 Host not found (authoritative)
.
所以,简化一下:
#include <boost/asio.hpp>
using namespace std::literals;
namespace ba = boost::asio;
using ba::ip::udp;
int main() {
ba::io_context io;
udp::resolver resolver{io};
auto endpoints = resolver.resolve(udp::v4(), "127.0.0.1", "6767");
udp::socket socket{io};
socket.open(udp::v4());
return socket.send_to(ba::buffer("asdf"sv), endpoints->endpoint());
}
有
nc -u -l -p 6767 & sleep 1; ./a.out
版画
asdf