开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SSL,串口,跨平台,支持可靠UDP,支持TCP自动拆包等 您所在的位置:网站首页 开源websocket框架 开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SSL,串口,跨平台,支持可靠UDP,支持TCP自动拆包等

开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SSL,串口,跨平台,支持可靠UDP,支持TCP自动拆包等

2023-08-13 22:19| 来源: 网络整理| 查看: 265

开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SSL,串口,跨平台,支持可靠UDP,支持TCP自动拆包等

C++开发网络通信程序时用asio是个不错的选择,但asio本身是一套函数集,自己还要处理诸如“通信线程池管理、连接及生命周期管理、多线程收发数据的同步保护等”。因此这里对asio进行了一层封装,大大简化了对asio的使用。代码使用了C++17相关功能,所以只能用在C++17以上。

代码大量使用了CRTP模板编程实现(没有使用virtual而用CRTP实现的静态多态),因此编译比较耗时,但执行效率相对较好一点。

项目地址:

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

QQ交流群:833425075 header only,不依赖boost库,不需要单独编译,在工程的Include目录中添加asio2路径,在源码中#include 即可使用; 支持tcp, udp, http, websocket, rpc, ssl, icmp, serial_port; 支持可靠UDP(基于KCP),支持SSL,支持从内存字符串加载SSL证书; TCP支持各种数据拆包功能(单个字符或字符串或用户自定义协议等);实现了数据报模式的TCP(类似WEBSOCKET); 跨平台,支持windows,linux,macos,arm,android,32位,64位等;在msvc gcc clang ndk mingw下编译通过; 基于C++17,基于asio (standalone asio或boost::asio均可); example目录包含大量示例代码,各种使用方法请参考示例代码; 与其它框架的一点区别: 目前看到的很多基于asio的框架的模式大都如下: tcp_server server; // 声明一个server server.run(); // 调用run函数,run函数是阻塞的,run之后怎么退出却不知道. 这种模式需要用户自己去处理程序退出后的逻辑,包括连接的正常关闭, 资源释放等问题,而这些问题自己处理起来是很烦琐的. asio2框架已经处理过了这些问题,你可以在如MFC的OnInitDialog等地方调用server.start(...), start(...)函数是非阻塞的,什么时候想退出了只需要server.stop()即可.stop()是阻塞的, stop时如果有未发送完的数据,会保证一定在数据发送完之后才会退出, tcp下也会保证所有连接都正常关闭以后才会退出,你不用考虑资源的正确释放等一系列琐碎的问题. TCP: 服务端: asio2::tcp_server server; server.bind_recv([&](std::shared_ptr & session_ptr, std::string_view s) { printf("recv : %zu %.*s\n", s.size(), (int)s.size(), s.data()); // 异步发送(所有发送操作都是异步且线程安全的) session_ptr->async_send(s); // 发送时指定一个回调函数,当发送完成后会调用此回调函数,bytes_sent表示实际发送的字节数, // 发送是否有错误可以用asio2::get_last_error()函数来获取错误码 // session_ptr->async_send(s, [](std::size_t bytes_sent) {}); }).bind_connect([&](auto & session_ptr) { session_ptr->no_delay(true); printf("client enter : %s %u %s %u\n", session_ptr->remote_address().c_str(), session_ptr->remote_port(), session_ptr->local_address().c_str(), session_ptr->local_port()); // 可以用session_ptr这个会话启动一个定时器,这个定时器是在这个session_ptr会话的数据收 // 发线程中执行的,这对于连接状态的判断或其它需求很有用(尤其在UDP这种无连接的协议中,有 // 时需要在数据处理过程中使用一个定时器来延时做某些操作,而且这个定时器还需要和数据处理 // 在同一个线程中安全触发) //session_ptr->start_timer(1, std::chrono::seconds(1), []() {}); }).bind_disconnect([&](auto & session_ptr) { printf("client leave : %s %u %s\n", session_ptr->remote_address().c_str(), session_ptr->remote_port(), asio2::last_error_msg().c_str()); }); server.start("0.0.0.0", "8080"); // 按\n自动拆包(可以指定任意字符) //server.start("0.0.0.0", "8080", '\n'); // 按\r\n自动拆包(可以指定任意字符串) //server.start("0.0.0.0", "8080", "\r\n"); // 按自定义规则自动拆包(match_role请参考example代码)(用于对用户自定义的协议拆包) // 对自定义协议拆包时,match_role如何使用的详细说明请看:https://blog.csdn.net/zhllxt/article/details/104772948 //server.start("0.0.0.0", "8080", match_role('#')); // 每次接收固定的100字节 //server.start("0.0.0.0", "8080", asio::transfer_exactly(100)); // 数据报模式的TCP,无论发送多长的数据,双方接收的一定是相应长度的整包数据 //server.start("0.0.0.0", "8080", asio2::use_dgram); 客户端: asio2::tcp_client client; // 客户端在断开时默认会自动重连 // 禁止自动重连 //client.auto_reconnect(false); // 启用自动重连 默认在断开连接后延时1秒就会开始重连 //client.auto_reconnect(true); // 启用自动重连 并设置自定义的延时 client.auto_reconnect(true, std::chrono::seconds(3)); client.bind_connect([&]() { if (asio2::get_last_error()) printf("connect failure : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str()); else printf("connect success : %s %u\n", client.local_address().c_str(), client.local_port()); // 如果连接成功 就可以调用异步发送函数发送数据了 if (!asio2::get_last_error()) client.async_send(""); // 如果在通信线程中调用同步发送函数会退化为异步调用(这里的bind_connect的回调函数就位于通信线程中) // client.send("abc"); }).bind_disconnect([]() { printf("disconnect : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str()); }).bind_recv([&](std::string_view sv) { printf("recv : %zu %.*s\n", sv.size(), (int)sv.size(), sv.data()); client.async_send(sv); }) //// 绑定全局函数 //.bind_recv(on_recv) //// 绑定成员函数(具体请查看example代码) //.bind_recv(std::bind(&listener::on_recv, &lis, std::placeholders::_1)) //// 按lis对象的引用来绑定成员函数(具体请查看example代码) //.bind_recv(&listener::on_recv, lis) //// 按lis对象的指针来绑定成员函数(具体请查看example代码) //.bind_recv(&listener::on_recv, &lis) ; // 异步连接服务端 //client.async_start("0.0.0.0", "8080"); // 同步连接服务端 client.start("0.0.0.0", "8080"); // 连接成功后,可以调用发送函数(这里是主线程不在通信线程中) // 同步发送和异步发送可以混用,是线程安全的(一定会在A发送完之后才会发送B) std::size_t bytes_sent = client.send("abc"); // 同步发送函数的返回值为发送的字节数 可以用get_last_error()查看是否发生错误 if(asio2::get_last_error()) { printf("同步发送数据失败:%s\n", asio2::last_error_msg().data()); } // 按\n自动拆包(可以指定任意字符) //client.async_start("0.0.0.0", "8080", '\n'); // 按\r\n自动拆包(可以指定任意字符串) //client.async_start("0.0.0.0", "8080", "\r\n"); // 按自定义规则自动拆包(match_role请参考example代码)(用于对用户自定义的协议拆包) // 对自定义协议拆包时,match_role如何使用的详细说明请看:https://blog.csdn.net/zhllxt/article/details/104772948 //client.async_start("0.0.0.0", "8080", match_role); // 每次接收固定的100字节 //client.async_start("0.0.0.0", "8080", asio::transfer_exactly(100)); // 数据报模式的TCP,无论发送多长的数据,双方接收的一定是相应长度的整包数据 //client.start("0.0.0.0", "8080", asio2::use_dgram); // 发送时也可以指定use_future参数,然后通过返回值future来阻塞等待直到发送完成,发送结果的错误码和发送字节数 // 保存在返回值future中(注意,不能在通信线程中用future去等待,这会阻塞通信线程进而导致死锁) // std::future future = client.async_send("abc", asio::use_future); UDP: 服务端: asio2::udp_server server; // ... 绑定监听器(请查看example代码) server.start("0.0.0.0", "8080"); // 常规UDP //server.start("0.0.0.0", "8080", asio2::use_kcp); // 可靠UDP 客户端: asio2::udp_client client; // ... 绑定监听器(请查看example代码) client.start("0.0.0.0", "8080"); //client.async_start("0.0.0.0", "8080", asio2::use_kcp); // 可靠UDP RPC: 服务端: // 全局函数示例,当服务端的RPC函数被调用时,如果想知道是哪个客户端调用的,将这个RPC函数的第一 // 个参数设置为连接对象的智能指针即可(如果不关心是哪个客户端调用的,第一个参数可以不要),如下: int add(std::shared_ptr& session_ptr, int a, int b) { return a + b; } // rpc默认是按照"数据长度+数据内容"的格式来发送数据的,因此客户端可能会恶意组包,导致解析出的 // "数据长度"非常长,此时就会分配大量内存来接收完整数据包.避免此问题的办法就是是指定缓冲区最 // 大值,如果发送的数据超过缓冲区最大值,就会将该连接直接关闭.所有tcp udp http websocket,server // client 等均支持这个功能. asio2::rpc_server server( 512, // 接收缓冲区的初始大小 1024, // 接收缓冲区的最大大小 4 // 多少个并发线程 ); // ... 绑定监听器(请查看example代码) // 绑定RPC全局函数 server.bind("add", add); // 绑定RPC成员函数 server.bind("mul", &A::mul, a); // 绑定lambda表达式 server.bind("cat", [&](const std::string& a, const std::string& b) { return a + b; }); // 绑定成员函数(按引用) a的定义请查看example代码 server.bind("get_user", &A::get_user, a); // 绑定成员函数(按指针) a的定义请查看example代码 server.bind("del_user", &A::del_user, &a); // 服务端也可以调用客户端的RPC函数(通过连接对象session_ptr) session_ptr->async_call([](int v) { printf("sub : %d err : %d %s\n", v, asio2::last_error_val(), asio2::last_error_msg().c_str()); }, std::chrono::seconds(10), "sub", 15, 6); //server.start("0.0.0.0", "8080"); 客户端: asio2::rpc_client client; // ... 绑定监听器(请查看example代码) // 不仅server可以绑定RPC函数给client调用,同时client也可以绑定RPC函数给server调用。请参考example代码。 client.start("0.0.0.0", "8080"); // 同步调用RPC函数 int sum = client.call(std::chrono::seconds(3), "add", 11, 2); printf("sum : %d err : %d %s\n", sum, asio2::last_error_val(), asio2::last_error_msg().c_str()); // 异步调用RPC函数, // 第一个参数是回调函数,当调用完成或超时会自动调用该回调函数 // 第二个参数是调用超时,可以不填,如果不填则使用默认超时 // 第三个参数是rpc函数名,之后的参数是rpc函数的参数 client.async_call([](int v) { // 如果超时或发生其它错误,可以通过asio2::get_last_error()等一系列函数获取错误信息 printf("sum : %d err : %d %s\n", v, asio2::last_error_val(), asio2::last_error_msg().c_str()); }, "add", 10, 20); // 上面的调用方式的参数位置很容易搞混,因此也支持链式调用,如下(其它示例请查看example): client.timeout(std::chrono::seconds(5)).async_call("mul", 2.5, 2.5).response( [](double v) { std::cout


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有