基于c++和asio的网络编程框架asio2教程基础篇:1、基本概念和使用说明

基于c++和asio的网络编程框架asio2教程基础篇:1、基本概念和使用说明

由于asio2没有写技术文档,因此打算写几篇文章介绍一下如何使用它,主要是针对新手。

1、asio2如何使用?

  • asio2这个框架的使用很简单,以VS2017举例:在VS2017的项目上点右键 - 属性 - C/C++ - 常规 - 附加包含目录,将asio2-master/3rd和asio2-master/include这两个目录添加进去(假定你在github下载的是master分支的压缩包),然后在你的源代码中#include <asio2/asio2.hpp>即可开始使用了
  • 在/asio2/example目录下有大量的示例代码,支持cmake,可使用cmake生成visual studio的解决方案后,直接用visual studio打开去看示例即可。
  • 关于asio库的使用方法,网上能搜到大量的文章和代码,这里不介绍了。我主要是通过看boost\libs\asio\example下的官方示例来学习asio的。

2、asio2是什么?

  • asio2是在C++17基础上,基于asio库进行二次封装,编写的一个网络编程的框架,目的是想降低对asio本身使用的难度。

3、为什么不直接使用asio?为什么要做asio2?

  • 因为asio“是一个库,而不是一个框架”,直接使用asio库的话,就要使用它的很多的API,要处理很多细节,所以这里才进行二次包装,又做了一个“框架”,目的就是把他的各种API再包装一遍,使用起来更简单,代码量大大减少,否则直接使用asio要考虑的细节就会多很多。

4、直接使用asio和使用asio2有什么区别?

  • 我觉得区别主要还是在于使用asio的话需要自己去做这些工作:“在程序退出时,连接如何正常关闭,如何保证未发送完的数据一定是在发送之后程序才会退出”,也就是说程序如何优雅的退出这个问题。
  • 看起来这个功能似乎不难做,但还是花了我大量时间去思考和试验。使用asio2的话就不需要再考虑这些问题了,比如使用tcp,那么构造一个tcp_server对象,然后直接调用tcp_server.start()启动服务,退出时直接调用tcp_server.stop()关闭服务即可,所有的连接正常关闭,数据发送,资源释放等这些问题,都不需要你再去考虑了。在tcp_server.stop()函数结束之前,会确保上面的这些问题完美处理之后,stop()函数才会结束的(stop()函数是阻塞的,假如有1千个连接,会一直阻塞到这个1千个连接都正常关闭了才会结束阻塞)。

5、asio2有什么优点?

  • 除了上面第2条所说的优雅的退出之外,其它的优点大概有以下这些:
  • 接口比较简单,主要有start-启动服务,stop-停止服务,bind_recv-绑定接收数据的回调函数,bind_connect-绑定连接成功的回调函数,…等等。
  • 支持tcp,udp,http,websocket,rpc,ssl,串口等,而且形成了统一的接口,也就是这些组件的接口函数基本都一样。
  • header-only,不需要单独去编译,只要包含一下头文件,就可以用了。(我个人对非header only而是需要编译的库深恶痛绝,跟非header-only的库需要配置各种编译选项等一系列问题相比,我觉得header-only的编译慢点完全不是问题)

6、asio2有什么缺点?

  • 只支持C++,且只支持C++17以上(包含C++17)。
  • 纯异步的,对同步支持非常不好。(asio2这个框架只针对大部分的通用情形去考虑的)

7、asio2的实现思路

  • 大致思路就是使用C++的CRTP模板技术,和多重派生,使用多个小模块拼接组合成一个完整组件的方式(看代码中tcp,udp,http,websocket,rpc,ssl这些组件都是通过继承多个类来完成的),减少代码量。
  • 相比virtual,使用CRTP的好处是效率更高一些,缺点是源码看起来更晦涩难懂了。
  • 关于CRTP编译期多态和virtual运行期多态的效率可以看看这个评测:https://eli.thegreenplace.net/2013/12/05/the-cost-of-dynamic-virtual-calls-vs-static-crtp-dispatch-in-c/

8、关于asio2的头文件路径包含的详细说明

解压github下载的asio2压缩包之后,里面有3rd,include,test,example等文件夹,其中3rd文件夹包含了asio,cereal,fmt等一些开源库,asio2的代码需要用到这些开源库;include文件夹里面又包含了一个asio2文件夹,这个asio2文件夹是真正的asio2的相关代码;test文件夹是性能测试和单元测试相关的代码;example文件夹是各种使用示例代码;

最新版本的asio2代码可以使用cmake直接生成vs的解决方案等,打开vs解决方案即可直接编译,不需要任何其它额外设置(至于cmake如何使用,不会的话自己网络搜索)。

如果你对vs项目的头文件路径包含不熟悉,你可以直接参考cmake生成的vs解决方案里的头文件路径包含是如何设置的。

通常,如果你建立了自己的项目的vs解决方案(而不是使用的asio2里的cmake生成的解决方案),那么你需要在你的vs头文件路径包含中添加以下两项(假定你在github下载的是master分支的压缩包):asio2-master/3rd和asio2-master/include

如果你对项目头文件路径包含很熟悉,比如你有你自己的第三方库目录(比如你习惯把你自己用到的第三方库都放在这个目录下),那么你只需要把asio2-master/3rd里面的全部文件(是asio2-master/3rd里面的文件而不是asio2-master/3rd这个目录本身),和asio2-master/include里面的全部文件,放到你自己的第三方库目录即可。

9、在DLL中使用asio2的注意事项

1、不能在Windows DLL中直接声明一个asio2的server或client对象,会导致死锁。

比如像下面这样在dllmain.cpp中直接声明了一个asio2全局对象:

asio2::tcp_client client;

这就会导致在DLL的入口函数DllMain中出现死锁,死锁的原因是std::thread引起的,参考asio2/util/thread_pool.hpp开头的说明

需要用下面的方式,即声明一个指针,然后自己在DLL中做一个导出函数如Init(),在EXE中手动调用你的DLL中的导出函数Init();
然后在Init()中创建指针。如:

// 在dllmain.cpp先声明一个全局的指针对象
std::shared_ptr<asio2::tcp_client> client;
// 这是你dll中的导出函数Init
void Init()
{
    client = std::make_shared<asio2::tcp_client>();
}

// 然后在你的EXE中手动调用这个Init导出函数即可。

2、不能在Windows Dll的DLL_PROCESS_DETACH块中调用asio2的server或client对象的stop函数,会导致stop函数永远阻塞无法返回。

原因是由于在DLL_PROCESS_DETACH时,通过PostQueuedCompletionStatus投递的IOCP事件永远得不到执行。

解决办法依然是自己在DLL中做一个导出函数如Uninit(),在EXE中手动调用你的DLL中的导出函数Uninit();在Uninit函数中调用对象的stop函数;如:

// 这是你dll中的导出函数Uninit
void Uninit()
{
    client->stop();
}

项目地址:

github : https://github.com/zhllxt/asio2
码云 : https://gitee.com/zhllxt/asio2

最后编辑于2022-06-23

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,我可以为您提供一个简单的例子。 首先,您需要安装 crow 和 asio 库。您可以使用以下命令来安装它们: ``` $ sudo apt-get install libasio-dev $ git clone https://github.com/ipkn/crow.git $ cd crow $ make ``` 接下来,您可以创建一个名为 `webserver.cpp` 的文件,并将以下代码复制粘贴到该文件中: ```c++ #include "crow.h" #include <iostream> int main() { crow::SimpleApp app; CROW_ROUTE(app, "/") ([](){ return "Hello world!"; }); app.port(18080).run(); } ``` 这个应用程序将创建一个基本的 web 服务器,它将监听端口 18080,并在收到请求时返回 "Hello world!"。 现在,让我们来看看如何使用 asio 来实现网络编程。我们将使用 asio 来创建一个 TCP 客户端,该客户端将连接到远程服务器并发送一条消息。 在 `webserver.cpp` 文件的顶部添加以下代码: ```c++ #include <asio.hpp> #include <string> ``` 然后,在 `main` 函数中添加以下代码: ```c++ // 创建一个 io_service 对象 asio::io_service io_service; // 创建一个 TCP resolver 对象,并解析远程主机和端口 asio::ip::tcp::resolver resolver(io_service); asio::ip::tcp::resolver::query query("www.example.com", "80"); auto endpoints = resolver.resolve(query); // 创建一个 TCP socket 对象,并连接到远程主机 asio::ip::tcp::socket socket(io_service); asio::connect(socket, endpoints); // 发送消息到远程主机 std::string message = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"; asio::write(socket, asio::buffer(message)); ``` 这个代码片段将创建一个 `io_service` 对象,并使用 `resolver` 和 `socket` 对象来连接到远程主机。然后,它将发送一条消息到远程主机。 现在,您可以编译并运行代码。在终端中,导航到 `webserver.cpp` 文件所在的目录,并执行以下命令: ``` $ g++ -std=c++11 webserver.cpp -o webserver -lboost_system -lpthread $ ./webserver ``` 这将编译并运行您的应用程序。现在,您可以在浏览器中打开 `http://localhost:18080` 来查看您的 web 服务器是否正常工作,并在终端中查看输出以查看来自远程服务器的响应。 希望这可以帮助您入门 crow 和 asio

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值