UVW源码漫谈(一)

UVW源码漫谈(一)博客园是个非常好的学习知识的地方 相信有很多人跟我一样 园龄 3 年 从博客园不知道拷了多少代码 看了多少博客 自己却一篇博客都没写过 真是罪过 这次准备写几篇关于这个项目源码的阅读和理解的文章 大家一起相互学习学习 我可能不会单单就写源码一类的东西 还会做很多扩展 比如新的 c 的语法 其他的一些工具等等 各位看官不要嫌烦 咱们又不是什么大牛 遇到文中有歧义 不对之处 请在评论

  博客园是个非常好的学习知识的地方,相信有很多人跟我一样,园龄3年,从博客园不知道拷了多少代码,看了多少博客,自己却一篇博客都没写过。真是罪过。

  这次准备写几篇关于这个项目源码的阅读和理解的文章,大家一起相互学习学习,我可能不会单单就写源码一类的东西,还会做很多扩展,比如新的c++的语法,其他的一些工具等等,各位看官不要嫌烦。咱们又不是什么大牛,遇到文中有歧义,不对之处,请在评论区留言,咱们一起讨论,再做改进,避免误人子弟。

  废话不多说,现在开始。

 

  最近在看一个项目 uvw 的源码,可能很多人不知道这个东西。搞过一些网络编程的人应该知道 libuv,uvw 是我在github上找到的一个用c++封装 libuv 的项目,源代码作者也在持续更新中。

  简单的介绍一下:

  libuv:是一个跨平台的网络库,具体可以参考博客:http://www.cnblogs.com/haippy/archive/2013/03/17/2963995.html 以及博主的libuv系列的文章。

  uvw:用 c++14 对libuv的封装,作者应该是个外国人,代码质量应该没的说,尤其是注释,非常详尽,值得我等菜鸟学习。

     github地址:https://github.com/skypjack/uvw

  

  首先得把代码搞出来,

  1、直接下载,地址:https://codeload.github.com/skypjack/uvw/zip/master

  2、git clone https://github.com/skypjack/uvw.git

 

  注:文件路径写法: ./src/uvw.hpp  当前目录为代码根目录。

  代码基本上在src文件中,切到src,对,你没有看错,全是hpp文件,所以如果你要用这个库,直接把src拷到你工程里就行了。用起来可以说是非常方便,但是你的工程不要忘了包含libuv的头文件和链接libuv库。另外uvw对libuv的版本也有限制,可以在github的tag中查看libuv对应的版本,如果你是用方法2,可以用命令”git tag -l“查看。(关于git这个东西,如果有看官还不了解的,可以参考菜鸟教程:http://www.runoob.com/git/git-tutorial.html  或者去git官网,有非常详细的资料)

  UVW源码漫谈(一)

一、先来看看怎么用

  拷一段代码(./test/main.cpp):

 1 #include "../src/uvw.hpp"  2 #include <cassert>  3 #include <iostream>  4 #include <memory>  5 #include <chrono>  6  7  8 void listen(uvw::Loop &loop) {  9 std::shared_ptr<uvw::TcpHandle> tcp = loop.resource<uvw::TcpHandle>(); //创建一个TcpHandle  10  11 tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TcpHandle &) { //注册错误发生函数,  12 std::cout << "error " << std::endl;  13  });  14  15 tcp->once<uvw::ListenEvent>([](const uvw::ListenEvent &, uvw::TcpHandle &srv) { //注册监听事件函数  16 std::cout << "listen" << std::endl;  17  18 std::shared_ptr<uvw::TcpHandle> client = srv.loop().resource<uvw::TcpHandle>(); //创建一个TcpHandle,用于新的client连接  19  20 client->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TcpHandle &) { //为client注册错误发生函数  21 std::cout << "error " << std::endl;  22  });  23  24 client->on<uvw::CloseEvent>([ptr = srv.shared_from_this()](const uvw::CloseEvent &, uvw::TcpHandle &) { //注册client关闭函数  25 std::cout << "close" << std::endl;  26 ptr->close(); //这里当client被关闭时,也会关闭server  27  });  28  29 srv.accept(*client); //server accept  30  31 uvw::Addr local = srv.sock();  32 std::cout << "local: " << local.ip << " " << local.port << std::endl;  33  34 uvw::Addr remote = client->peer();  35 std::cout << "remote: " << remote.ip << " " << remote.port << std::endl;  36  37 client->on<uvw::DataEvent>([](const uvw::DataEvent &event, uvw::TcpHandle &) { //注册client接收数据事件函数  38 std::cout.write(event.data.get(), event.length) << std::endl; //event中已经保存有读取的数据,可以直接使用  39 std::cout << "data length: " << event.length << std::endl;  40  });  41  42 client->on<uvw::EndEvent>([](const uvw::EndEvent &, uvw::TcpHandle &handle) { //注册client数据读取结束函数,当socket没有数据可读时会发送该事件  43 std::cout << "end" << std::endl;  44 int count = 0;  45 handle.loop().walk([&count](uvw::BaseHandle &) { ++count; }); //获取主loop中活跃的套接字,这里有server和client两个  46 std::cout << "still alive: " << count << " handles" << std::endl;  47 handle.close(); //关闭client连接  48  });  49  50 client->read(); //开始读取数据,这里和uv_read_start的效果相同, 这里和上面的注册事件操作,调用时是不分先后顺序的。  51  });  52  53 tcp->once<uvw::CloseEvent>([](const uvw::CloseEvent &, uvw::TcpHandle &) {  54 std::cout << "close" << std::endl;  55  });  56  57 tcp->bind("127.0.0.1", 4242); //bind,这里支持IPv4和IPv6,bind为一个模版函数  58 tcp->listen(); //listen  59 }  60  61  62 void conn(uvw::Loop &loop) {  63 auto tcp = loop.resource<uvw::TcpHandle>(); //下面的基本和listen中类似,不多做注释  64  65 tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TcpHandle &) {  66 std::cout << "error " << std::endl;  67  });  68  69 tcp->once<uvw::WriteEvent>([](const uvw::WriteEvent &, uvw::TcpHandle &handle) {  70 std::cout << "write" << std::endl;  71  handle.close();  72  });  73  74 tcp->once<uvw::ConnectEvent>([](const uvw::ConnectEvent &, uvw::TcpHandle &handle) {  75 std::cout << "connect" << std::endl;  76  77 auto dataTryWrite = std::unique_ptr<char[]>(new char[1]{ 'a' }); //以下操作为向server发送数据  78 int bw = handle.tryWrite(std::move(dataTryWrite), 1);  79 std::cout << "written: " << ((int)bw) << std::endl;  80  81 auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b', 'c' });  82 handle.write(std::move(dataWrite), 2);  83  });  84  85 tcp->once<uvw::CloseEvent>([](const uvw::CloseEvent &, uvw::TcpHandle &) {  86 std::cout << "close" << std::endl;  87  });  88  89 tcp->connect("127.0.0.1", 4242);  90 }  91  92 void g() {  93 auto loop = uvw::Loop::getDefault(); //获取默认事件循环  94 listen(*loop);  95 conn(*loop);  96 loop->run(); //开始事件循环  97 loop = nullptr;  98 }  99 100 int main() { 101  g(); 102 }

 

  (话说怎么没有我喜欢的代码字体的)

  好像挺长的,这边结构看上去还算是比较清晰。

 

二、仔细看看

  1、server端操作

    listen()函数基本上包含了所有server端的操作,基本流程就是:

      创建TcpHandle(第9行) –> bind(第57行) –> listen(第58行)

 

    在ListenEvent中,可以看到第18行,又创建了一个TcpHandle  client,用来接收客户端的连接:

      创建TcpHandle(第18行) –> accept(第29行) –> read(第50行)

 

    除了这些其他的代码就是事件处理的过程,事件处理都是用的Lambda表达式来写的,比如:

1 tcp->on<uvw::ErrorEvent>([](const uvw::ErrorEvent &, uvw::TcpHandle &) { //注册错误发生函数, 2 std::cout << "error " << std::endl; 3 });

    Lambda都有两个参数:

      {Event}:事件,在代码中可以看很多事件类型,比如CloseEvent,ConnectEvent等(看名字应该就知道是什么事件了)

      {Handle}:Source类型,这里可能还会有 UdpHandle等等libuv中出现的类型。以后看到源码再谈。

    后经运行调试,事件处理匿名函数里的{Handle}和创建的TcpHandle其实是相同的。

 

  2、client端操作

    conn函数里基本就是创建一个TcpHandle,然后调用connect连接到服务器,其他的就是相关的事件。

    另外就是client的数据发送:

1 std::cout << "connect" << std::endl; 2 3 auto dataTryWrite = std::unique_ptr<char[]>(new char[1]{ 'a' }); //以下操作为向server发送数据 4 int bw = handle.tryWrite(std::move(dataTryWrite), 1); 5 std::cout << "written: " << ((int)bw) << std::endl; 6 7 auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b', 'c' }); 8 handle.write(std::move(dataWrite), 2);

    可以看到调用了两个数据写入函数,tryWrite和write,相当于uv_write 和 uv_try_write,我给出作者对tryWrite的注释:    

/
 * @brief Queues a write request if it can be completed immediately.
 *
 * Same as `write()`, but won’t queue a write request if it can’t be
 * completed immediately.<br/>
 * An ErrorEvent event will be emitted in case of errors.
 *
 * @param data The data to be written to the stream.
 * @param len The lenght of the submitted data.
 * @return Number of bytes written.
 */
    意思就是tryWrite也会发送数据,但是不会立即完成,也不会保证把数据全部一次性发送完。而write会将没发送完的数据再次加到loop中等待下次发送。


  3、总结

    可以看出来,作者用大量的Lambda来代替了libuv中的各种回调,相比之下,用Lambda,可读性增加了很多。

    另外代码中使用了大量的模板函数来区分事件类型,作者源代码里应该使用了很多泛型,

 

三、相关知识

  1、Lambda

    Lambda又叫做匿名函数,这是个博客园帖子,可以稍微学习或者回顾一下:http://www.cnblogs.com/langzou/p/5962033.html

    PS:这个匿名函数的英文名,有一堆拼写:Lamda, Lamba,Lamdba,Ladbda。。。真的是千奇百怪。

      这里给大家强调一下,虽然名字到底怎么写对咱们学习东西没什么太大影响,但是本着严谨的态度,他的英文名正确拼写应该是

        Lambda    读音:lan b(m) da(兰木达)[‘læmdə]

      它是‘λ’的音译,百度百科上也是这个拼写,在《C++ Primer 第5版》的346页,也可以看到,所以大家以后不要记错哦,避免被人笑话了。哈哈。

    

    在第24行中:

1 client->on<uvw::CloseEvent>([ptr = srv.shared_from_this()](const uvw::CloseEvent &, uvw::TcpHandle &) { //注册client关闭函数 2 std::cout << "close" << std::endl; 3 ptr->close(); //这里当client被关闭时,也会关闭server 4 });

    大家有没有注意到这边 ptr = srv.shared_from_this() 是个什么东东?

    Lambda中 [] 不应该是用来捕获外部变量的吗,怎么这边好像是定义了一个ptr变量,并用shared_from_this()来给它初始化了。但是很明显这个ptr并没有参数类型,在上下文中也没有对ptr的声明。是不是非常奇怪。

    查阅了大量书籍资料后,在 http://zh.cppreference.com/w/cpp/language/lambda 中发现下面一段:

 1 带初始化器的捕获,行动如同它声明并显示捕获以类型 auto 声明的变量,变量的声明性区域是 lambda 表达式体(即它不在其初始化器的作用域中),除了:  2 若捕获以复制,则闭包的非静态数据成员是另一种指代该自动变量的方式。  3 若捕获以引用在,则引用变量的生存期在闭包对象的生存期结束时结束。  4 这用于捕获仅移动类型,以例如 x = std::move(x) 的捕获  5 int x = 4;  6 auto y = [&r = x, x = x + 1]()->int  7  {  8 r += 2;  9 return x * x; 10 }(); // 更新 ::x 为 6 并初始化 y 为 25 。

    可以看出,用的就是这种带初始化器的捕获,这是在c++14中新添加的特性

    在第一行中,可以知道,这种带初始化器的捕获会自动将变量声明为auto类型,并且可以对声明的变量进行初始化。切记,是初始化。对于上面的例子,如果写成这样:

1 int x = 4; 2 auto y = [&r = x, r = x + 1]()->int //错误 3  { 4 r += 2; 5 return x * x; 6 }(); 

    是错误的。另外如果你不初始化,也会产生编译错误。

    现在再看源代码第24行的代码,应该就没什么问题了吧?

    在了解这个之后我又找到一篇介绍这种捕获类型的文章:http://blog.csdn.net/big_yellow_duck/article/details/

    大家可以一起学习参考一下,看来我也得买本《Effective Modern C++》来看看了。

 

  2、智能指针

    这我就不介绍了,还是博客园的文章,大家可以学习或者回顾一下:http://www.cnblogs.com/329914874/p/6653412.html    

 

四、下一篇

  感谢各位看官还能看到这里,我的这个文笔不是很好,真是委屈各位了。

  接下来一篇会聊到UVW里的一个基础类,Emitter

  最近时间不太宽裕,可能要等个几天。

  

转载于:https://www.cnblogs.com/yxfangcs/p/7527658.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232439.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 华为电脑如何投屏到电视linux,华为mate10/mate10pro怎么投屏至电视或电脑上面?「建议收藏」

    一、使用华为2代DOCK实现手机连接大屏说明手机投屏输出接口为Type-C,支持通过转换器转换成标准的DP、HDMI、MiniDP、VGA、DVI等接口。考虑到设备的兼容性和信号的转换,请优先选择DP、HDMI接口。1.打开大屏显示器;2.将VGA直连线(两端都是VGA接口)的一端连接大屏,另一端连接至华为2代DOCK;3.将华为2代DOCK(扩展坞)的Type-C接口(USB-C接口)连接到华…

    2022年4月6日
    733
  • 4.4.2分类模型评判指标(一) – 混淆矩阵(Confusion Matrix)

    4.4.2分类模型评判指标(一) – 混淆矩阵(Confusion Matrix)简介混淆矩阵是ROC曲线绘制的基础,同时它也是衡量分类型模型准确度中最基本,最直观,计算最简单的方法。一句话解释版本:混淆矩阵就是分别统计分类模型归错类,归对类的观测值个数,然后把结果放在一个表里展示出来。这个表就是混淆矩阵。数据分析与挖掘体系位置混淆矩阵是评判模型结果的指标,属于模型评估的一部分。此外,混淆矩阵多用于判断分类器(Classifier)的优劣,适用于…

    2022年5月14日
    54
  • Windows下 LaTeX安装教程 TeX Live 2021版[通俗易懂]

    Windows下 LaTeX安装教程 TeX Live 2021版[通俗易懂]latex版本众多,这里只介绍windows版本官方网站:TeXLive-TeXUsersGroup第一步,点击onDVD第二步:点击如图第三步:点击第四步:点击如下链接,等待下载完成第五步:双击或者右击Windows资源管理器第六步:点击安装,注意安装地址,建议不要放在C盘点击安装耐心等待。。。…

    2022年6月1日
    238
  • 2021sublime4113 激活码-激活码分享

    (2021sublime4113 激活码)JetBrains旗下有多款编译器工具(如:IntelliJ、WebStorm、PyCharm等)在各编程领域几乎都占据了垄断地位。建立在开源IntelliJ平台之上,过去15年以来,JetBrains一直在不断发展和完善这个平台。这个平台可以针对您的开发工作流进行微调并且能够提供…

    2022年3月27日
    221
  • Microsoft Visual Studio 2010 正式版下载[含旗舰版序列号](中、英文版)[通俗易懂]

    Microsoft Visual Studio 2010 正式版下载[含旗舰版序列号](中、英文版)[通俗易懂]前日(2010年4月12日),微软正式发布了MicrosoftVisualStudio2010,相信这是个让众多微软开发者们心情激动的一天吧。至于VS是啥米东东之类的就不再解释

    2022年7月20日
    22
  • JAVA英文文献_java毕业论文参考文献

    JAVA英文文献_java毕业论文参考文献JAVA编程思想英文参考文献和翻译时间:2016-11-1514:44来源:毕业论文虽然java是基于C++基础上的,但是它更是纯粹的面向对象语“Ifwespokeadifferentlanguage,wewouldperceiveasomewhatdifferentworld.”3670LudwigWittgenstein(1889-1951)Althoughit…

    2022年9月30日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号