您是否有不需要打开问题的问题?加入 gitter 频道。
如果您使用uvw并且想要表示感谢或支持该项目,请考虑成为赞助商。
你可以帮助我改变现状。非常感谢那些支持我并且今天仍然支持我的人。
uvw最初是一个仅包含标头的、基于事件的、小型且易于使用的libuv包装器,用现代 C++ 编写。
现在它终于可以作为可编译的静态库使用了。
基本思想是将libuv的C 风格接口包装在优雅的 C++ API 后面。
请注意, uvw忠实于libuv的 API,并且没有向其接口添加任何内容。出于同样的原因,该库的用户必须遵循与libuv相同的规则。
例如,句柄应在任何其他操作之前初始化,并在不再使用时关闭。
#include <uvw.hpp>#include <内存>void Listen(uvw::loop &loop) {
std::shared_ptr<uvw::tcp_handle> tcp = Loop.resource<uvw::tcp_handle>();
tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>();
客户端->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); });
客户端->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
srv.accept(*客户端);
客户端->read();
});
tcp->绑定(“127.0.0.1”, 4242);
tcp->listen();
}void conn(uvw::loop &loop) {auto tcp = Loop.resource<uvw::tcp_handle>();
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* 处理错误 */ });
tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b' , 'c' });
tcp.write(std::move(dataWrite), 2);
tcp.close();
});
tcp->connect(std::string{"127.0.0.1"}, 4242);
}int main() {自动循环 = uvw::loop::get_default();listen(*loop);conn(*loop);
循环->run();
}编写uvw的主要原因是 C++ 中不存在有效的libuv包装器。就这样。
为了能够使用uvw ,用户必须提供以下系统范围的工具:
至少支持 C++17 的全功能编译器。
libuv (具体版本取决于使用的uvw标签)
如果您使用meson ,将为您下载 libuv
编译测试和提取文档必须满足以下要求:
CMake 版本 3.13 或更高版本。
Doxygen 版本 1.8 或更高版本。
请注意, libuv是项目依赖项的一部分,在某些情况下可能会被CMake克隆(有关更多详细信息,请参阅下文)。
因此,用户不必安装它来运行测试或通过CMake编译uvw库。
您可以将uvw与介子一起使用,只需将其添加到项目中的subprojects目录中即可。
要从源代码编译uvw而不将其用作子项目,请在uvw源目录中运行:
$ meson setup build
如果需要静态库,请添加--default-library=static
$ cd build
$ meson compile
uvw是一个双模式库。它可以以其仅头文件的形式使用,也可以作为编译的静态库使用。
以下部分描述了在这两种情况下如何使uvw在您自己的项目中启动并运行。
要将uvw用作仅包含头文件的库,只需包含uvw.hpp头文件或其他uvw/*.hpp文件之一。
只需在文件顶部添加以下行即可:
#include <uvw.hpp>
然后将正确的-I参数传递给编译器以将src目录添加到包含路径中。
请注意,在这种情况下,用户需要正确设置libuv的包含目录和库搜索路径。
通过CMake使用时,为了方便起见,会导出uvw::uvw目标。
要将uvw用作编译库,请在包含项目之前在 cmake 中设置UVW_BUILD_LIBS选项。
此选项触发名为uvw::uvw-static目标的生成。为了方便起见, libuv的匹配版本也被编译并导出为uv::uv-static 。
如果您不使用或不想使用CMake ,您仍然可以编译所有.cpp文件并包含所有.h文件来完成工作。在这种情况下,用户需要正确设置libuv的包含目录和库搜索路径。
从libuv的标签v1.12.0开始, uvw遵循语义版本控制方案。
问题是任何版本的uvw都需要显式跟踪它所绑定的libuv版本。
因此,后者将被附加到uvw的版本中。举个例子:
vU.V.W_libuv-vX.Y
特别是,以下内容适用:
UVW是uvw的主要版本、次要版本和补丁版本。
XY是要引用的libuv版本(任何补丁版本都有效)。
换句话说,从现在开始,标签将如下所示:
v1.0.0_libuv-v1.12
uvw的分支master将是一个正在进行中的分支,遵循libuv的分支v1.x (至少只要它仍然是其主分支)。
该文档基于doxygen 。构建它:
$ cd build
$ cmake ..
$ make docs
API 参考将以 HTML 格式在目录build/docs/html中创建。
要使用您最喜欢的浏览器进行导航:
$ cd build
$ your_favorite_browser docs/html/index.html
最新版本也可以在线使用相同的版本,这是最后一个稳定的标签。
出于显而易见的原因,该文档主要受到官方 libuv API 文档的启发。
要编译和运行测试, uvw需要libuv和googletest 。
CMake将在编译其他内容之前下载并编译这两个库。
构建测试:
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
如果您还想测试libuv和其他依赖项,请省略-R uvw 。
使用uvw时只有一条规则:始终初始化资源并终止它们。
资源主要属于两个系列:句柄和请求。
句柄代表能够在活动时执行某些操作的长期对象。
请求(通常)代表通过句柄或独立执行的短期操作。
以下各节将简要解释初始化和终止此类资源的含义。
有关更多详细信息,请参阅在线文档。
初始化通常在幕后执行,甚至可以忽略,只要使用loop::resource成员函数创建句柄即可。
另一方面,句柄会保持活动状态,直到有人明确关闭它们为止。因此,如果用户忘记句柄,内存使用量将会增加。
因此规则很快就变成了永远握紧你的手。就像调用它们的close成员函数一样简单。
通常不需要初始化请求对象。不管怎样,推荐的创建请求的方式仍然是通过loop::resource成员函数。
只要请求与未完成的底层活动绑定,它们就会保持活动状态。这意味着用户不必明确放弃请求。
因此,规则很快就变得可以随意提出请求并忘记它。就像调用它们的成员函数一样简单。
使用uvw要做的第一件事是创建一个循环。如果默认的就足够了,那么很容易这样做:
自动循环= uvw::loop::get_default();
请注意,循环对象不需要显式关闭,即使它们提供close成员函数以防用户想要这样做。
可以使用run成员函数启动循环。下面的两个调用是等效的:
循环->run(); 循环->运行(uvw::loop::run_mode::DEFAULT);
可用模式有: DEFAULT 、 ONCE 、 NOWAIT 。请参阅libuv的文档以获取更多详细信息。
为了创建资源并将其绑定到给定的循环,只需执行以下操作:
自动 tcp = 循环->资源<uvw::tcp_handle>();
上面的行创建并初始化一个 tcp 句柄,然后返回指向该资源的共享指针。
用户应该检查指针是否已正确初始化:如果出现错误,则不会。
还可以创建未初始化的资源以供稍后初始化,如下所示:
自动 tcp = 循环->uninitialized_resource<uvw::tcp_handle>(); tcp->init();
所有资源还接受在任何情况下都不会被触及的任意用户数据。
用户可以通过data成员函数来设置和获取它们,如下所示:
资源->数据(std::make_shared<int>(42)); std::shared_ptr<void> 数据 = 资源->data();
资源需要std::shared_pointer<void>并返回它,因此欢迎任何类型的数据。
用户在调用data成员函数时可以显式指定除void之外的类型:
std::shared_ptr<int> data = resource->data<int>();
请记住,在上一节中,句柄将保持自身活动状态,直到调用它的close成员函数为止。
要了解哪些句柄仍然有效并绑定到给定循环,可以使用walk成员函数。它返回句柄及其类型。因此,建议使用overloaded ,以便能够拦截所有感兴趣的类型:
handle.parent().walk(uvw::重载{
[](uvw::timer_handle &h){ /* 此处定时器的应用程序代码 */ },
[](auto &&){ /* 忽略所有其他类型 */ }
});该函数也可用于完全通用的方法。例如,可以轻松关闭所有挂起的句柄,如下所示:
循环->步行([](auto &&h){ h.close(); });无需跟踪它们。
uvw提供了一种基于事件的方法,其中资源是附加侦听器的小型事件发射器。
将侦听器附加到资源是接收有关其操作的通知的推荐方法。
侦听器是void(event_type &, resource_type &)类型的可调用对象,其中:
event_type是它们设计的事件类型。
resource_type是发起事件的资源的类型。
这意味着以下函数类型都是有效的:
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
请注意,无需保留对资源的引用,因为每当发布事件时,它们都会将自己作为参数传递。
on成员函数是注册长时间运行的侦听器的方法:
resource.on<事件类型>(监听器)
要了解给定类型是否存在侦听器,该类提供了一个has函数模板。类似地, reset函数模板用于重置并断开侦听器(如果有)。还存在非模板版本的reset来清除整个发射器。
几乎所有资源在出现错误时都会发出error_event 。
所有其他事件均特定于给定资源并记录在 API 参考中。
下面的代码显示了如何使用uvw创建一个简单的 tcp 服务器:
自动循环 = uvw::loop::get_default();自动 tcp = 循环->资源<uvw::tcp_handle>();
tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* 出了问题 */ });
tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) {
std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>();
客户端->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); });
client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* 收到数据 */ });
srv.accept(*客户端);
客户端->read();
});
tcp->绑定(“127.0.0.1”, 4242);
tcp->listen();另请注意, uvw::tcp_handle已经支持IPv6开箱即用。
API 参考是有关资源及其方法的更多详细信息的推荐文档。
如果用户需要使用uvw尚未封装的功能,或者出于其他原因想要获取libuv定义的底层数据结构,几乎uvw中的所有类都可以直接访问它们。
请注意,除非用户确切知道自己在做什么以及存在哪些风险,否则不应直接使用此功能。原始是危险的,主要是因为循环、句柄或请求的生命周期管理完全由库控制,并且解决它可能会很快破坏事情。
话虽这么说,原始是使用raw成员函数的问题:
自动循环 = uvw::loop::get_default();自动 tcp = 循环->资源<uvw::tcp_handle>();uv_loop_t *raw = 循环->raw();uv_tcp_t *handle = tcp->raw() ;
使用原始方式需要您自担风险,但不要指望在出现错误时获得任何支持。
对基于uvw构建的其他工具和库感兴趣?那么您可能会发现以下内容很有用:
uvw_net :一个包含客户端集合(HTTP/Modbus/SunSpec)的网络库,还包括 dns-sd/mdns 等发现实现。
如果您愿意,请随意将您的工具添加到列表中。
如果您想做出贡献,请向分支主服务器发送补丁作为拉取请求。
检查贡献者列表,了解到目前为止谁参与了。
代码和文档 版权所有 (c) 2016-2024 Michele Caini。
徽标版权所有 (c) 2018-2021 理查德·卡塞雷斯。
根据 MIT 许可证发布的代码和文档。
徽标在 CC BY-SA 4.0 下发布。
如果你想支持这个项目,可以给我一杯浓缩咖啡。
如果您发现这还不够,请随时以您喜欢的方式帮助我。