asio2 开源基于asio的网络通信框架,支持TCP,UDP,HTTP,RPC,SSL开源项目

我要开发同款
匿名用户2019年06月05日
263阅读

技术信息

开源地址
https://gitee.com/zhllxt/asio2
授权协议
GPL

作品详情

开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等 

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

其中http和websocket部分用的是boost::beast,因此如果需要用到http或websocket的功能,则必须使用boost库,如果用不到http则直接使用独立的asio即可。在cofig.hpp中通过对ASIO_STANDALONE这个宏定义的开关,即可设置是使用boost::asio还是使用asiostadaloe.

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

github地址:https://github.com/zhllxt/asio2

码云地址:https://gitee.com/zhllxt/asio2

Aopesourcecross-platformc++libraryforetworkprogrammigbasedoasio,supportfortcp,udp,http,rpc,ssladsoo.

支持TCP,UDP,HTTP,WEBSOCKET,RPC,ICMP,SERIAL_PORT等;支持可靠UDP(基于KCP),支持SSL,支持从内存字符串加载SSL证书;TCP支持数据拆包功能(按指定的分隔符对数据自动进行拆包,保证用户收到的数据是一个完整的数据包);实现了TCP的数据报模式(类似WEBSOCKET);支持widows,liux,32位,64位;依赖asio(boost::asio或独立asio均可,若需要HTTP功能必须使用boost::asio),依赖C++17;代码采用hpp头文件方式,以源码级链入,无需编译,只需在工程的Iclude包含目录中添加asio2路径,然后在源码中#iclude<asio2/asio2.hpp>包含头文件即可;demo目录包含大量的示例工程(工程基于VS2017创建),各种使用方法请参考示例代码;TCP:

服务端:

asio2::tcp_serverserver;server.bid_recv([&server](std::shared_ptr<asio2::tcp_sessio>&sessio_ptr,std::strig_views){    sessio_ptr->o_delay(true);    pritf("recv:%u%.*s\",(usiged)s.size(),(it)s.size(),s.data());    //异步发送(所有发送操作都是异步且线程安全的)sessio_ptr->sed(s);//发送时指定一个回调函数,当发送完成后会调用此回调函数,bytes_set表示实际发送的字节数,//发送是否有错误可以用asio2::get_last_error()函数来获取错误码//sessio_ptr->sed(s,[](std::size_tbytes_set){});}).bid_coect([&server](auto&sessio_ptr){    pritf("clieteter:%s%u%s%u\",    sessio_ptr->remote_address().c_str(),sessio_ptr->remote_port(),        sessio_ptr->local_address().c_str(),sessio_ptr->local_port());    //可以用sessio_ptr这个会话启动一个定时器,这个定时器是在这个sessio_ptr会话的数据收    //发线程中执行的,这对于连接状态的判断或其它需求很有用(尤其在UDP这种无连接的协议中,有    //时需要在数据处理过程中使用一个定时器来延时做某些操作,而且这个定时器还需要和数据处理    //在同一个线程中安全触发)    //sessio_ptr->start_timer(1,std::chroo::secods(1),[](){});}).bid_discoect([&server](auto&sessio_ptr){pritf("clietleave:%s%u%s\",sessio_ptr->remote_address().c_str(),sessio_ptr->remote_port(),asio2::last_error_msg().c_str());});server.start("0.0.0.0","8080");//server.start("0.0.0.0","8080",'\');//按\自动拆包(可以指定任意字符)//server.start("0.0.0.0","8080","\r\");//按\r\自动拆包(可以指定任意字符串)//server.start("0.0.0.0","8080",match_role('#'));//按match_role指定的规则自动拆包(match_role请参考demo代码)(用于对用户自定义的协议拆包)//server.start("0.0.0.0","8080",asio::trasfer_exactly(100));//每次接收固定的100字节//server.start("0.0.0.0","8080",asio2::use_dgram);//数据报模式的TCP,无论发送多长的数据,双方接收的一定是相应长度的整包数据

客户端:

asio2::tcp_clietcliet;cliet.bid_coect([&](asio::error_codeec){if(asio2::get_last_error())pritf("coectfailure:%d%s\",asio2::last_error_val(),asio2::last_error_msg().c_str());elsepritf("coectsuccess:%s%u\",cliet.local_address().c_str(),cliet.local_port());cliet.sed("<abcdefghijklmopqrstovuxyz0123456789>");}).bid_discoect([](asio::error_codeec){pritf("discoect:%d%s\",asio2::last_error_val(),asio2::last_error_msg().c_str());}).bid_recv([&](std::strig_viewsv){pritf("recv:%u%.*s\",(usiged)sv.size(),(it)sv.size(),sv.data());cliet.sed(sv);})//.bid_recv(o_recv)//绑定全局函数//.bid_recv(std::bid(&listeer::o_recv,&lis,std::placeholders::_1))//绑定成员函数(具体请查看demo代码)//.bid_recv(&listeer::o_recv,lis)//按lis对象的引用来绑定成员函数(具体请查看demo代码)//.bid_recv(&listeer::o_recv,&lis)//按lis对象的指针来绑定成员函数(具体请查看demo代码);cliet.asyc_start("0.0.0.0","8080");//异步连接服务端//cliet.start("0.0.0.0","8080");//同步连接服务端//cliet.asyc_start("0.0.0.0","8080",'\');//按\自动拆包(可以指定任意字符)//cliet.asyc_start("0.0.0.0","8080","\r\");//按\r\自动拆包(可以指定任意字符串)//cliet.asyc_start("0.0.0.0","8080",match_role);//按match_role指定的规则自动拆包(match_role请参考demo代码)(用于对用户自定义的协议拆包)//cliet.asyc_start("0.0.0.0","8080",asio::trasfer_exactly(100));//每次接收固定的100字节//cliet.start("0.0.0.0","8080",asio2::use_dgram);//数据报模式的TCP,无论发送多长的数据,双方接收的一定是相应长度的整包数据//发送时也可以指定use_future参数,然后通过返回值future来阻塞等待直到发送完成,发送结果的错误码和发送字节数//保存在返回值future中(注意,不能在通信线程中用future去等待,这会阻塞通信线程进而导致死锁)//std::future<std::pair<asio::error_code,std::size_t>>future=cliet.sed("abc",asio::use_future);UDP:

服务端:

asio2::udp_serverserver;//...绑定监听器(请查看demo代码)server.start("0.0.0.0","8080");//常规UDP//server.start("0.0.0.0","8080",asio2::use_kcp);//可靠UDP

客户端:

asio2::udp_clietcliet;//...绑定监听器(请查看demo代码)cliet.start("0.0.0.0","8080");//cliet.asyc_start("0.0.0.0","8080",asio2::use_kcp);//可靠UDPRPC:

服务端:

asio2::rpc_serverserver;//...绑定监听器(请查看demo代码)Aa;//A的定义请查看demo代码server.bid("add",add);//绑定RPC全局函数server.bid("mul",&A::mul,a);//绑定RPC成员函数server.bid("cat",[&](coststd::strig&a,coststd::strig&b){retura+b;});//绑定lambda表达式server.bid("get_user",&A::get_user,a);//绑定成员函数(按引用)server.bid("del_user",&A::del_user,&a);//绑定成员函数(按指针)//server.start("0.0.0.0","8080",asio2::use_dgram);//使用TCP数据报模式作为RPC通信底层支撑,启动服务端时必须要使用use_dgram参数server.start("0.0.0.0","8080");//使用websocket作为RPC通信底层支撑(需要到rcp_server.hpp文件末尾代码中选择使用websocket)

客户端:

asio2::rpc_clietcliet;//...绑定监听器(请查看demo代码)//cliet.start("0.0.0.0","8080",asio2::use_dgram);//使用TCP数据报模式作为RPC通信底层支撑,启动服务端时必须要使用use_dgram参数cliet.start("0.0.0.0","8080");//使用websocket作为RPC通信底层支撑asio::error_codeec;//同步调用RPC函数itsum=cliet.call<it>(ec,std::chroo::secods(3),"add",11,2);pritf("sum:%derr:%d%s\",sum,ec.value(),ec.message().c_str());//异步调用RPC函数,第一个参数是回调函数,当调用完成或超时会自动调用该回调函数,如果超时或其它错误,//错误码保存在ec中,这里asyc_call没有指定返回值类型,则lambda表达式的第二个参数必须要指定类型cliet.asyc_call([](asio::error_codeec,itv){pritf("sum:%derr:%d%s\",v,ec.value(),ec.message().c_str());},"add",10,20);//这里asyc_call指定了返回值类型,则lambda表达式的第二个参数可以为auto类型cliet.asyc_call<it>([](asio::error_codeec,autov){pritf("sum:%derr:%d%s\",v,ec.value(),ec.message().c_str());},"add",12,21);//返回值为用户自定义数据类型(user类型的定义请查看demo代码)useru=cliet.call<user>(ec,"get_user");pritf("%s%d",u.ame.c_str(),u.age);for(auto&[k,v]:u.purview){pritf("%d%s",k,v.c_str());}pritf("\");u.ame="hameimei";u.age=((it)time(ullptr))%100;u.purview={{10,"get"},{20,"set"}};//如果RPC函数的返回值为void,则用户回调函数只有一个参数即可cliet.asyc_call([](asio::error_codeec){},"del_user",std::move(u));HTTP:服务端:asio2::http_serverserver;server.bid_recv([&](std::shared_ptr<asio2::http_sessio>&sessio_ptr,http::request<http::strig_body>&req){//在收到http请求时尝试发送一个文件到对端{//如果请求是非法的,直接发送错误信息到对端并返回if(req.target().empty()||req.target()[0]!='/'||req.target().fid("..")!=beast::strig_view::pos){sessio_ptr->sed(http::make_respose(http::status::bad_request,"Illegalrequest-target"));sessio_ptr->stop();//同时直接断开这个连接retur;}//Buildthepathtotherequestedfilestd::strigpath(req.target().data(),req.target().size());path.isert(0,std::filesystem::curret_path().strig());if(req.target().back()=='/')path.apped("idex.html");//打开文件beast::error_codeec;http::file_body::value_typebody;body.ope(path.c_str(),beast::file_mode::sca,ec);//如果打开文件失败,直接发送错误信息到对端并直接返回if(ec==beast::errc::o_such_file_or_directory){sessio_ptr->sed(http::make_respose(http::status::ot_foud,std::strig_view{req.target().data(),req.target().size()}));retur;}//Cachethesizesiceweeeditafterthemoveautocostsize=body.size();//生成一个文件形式的http响应对象,然后发送给对端http::respose<http::file_body>res{std::piecewise_costruct,std::make_tuple(std::move(body)),std::make_tuple(http::status::ok,req.versio())};res.set(http::field::server,BOOST_BEAST_VERSION_STRING);res.set(http::field::cotet_type,http::extesio_to_mimetype(path));res.cotet_legth(size);res.keep_alive(req.keep_alive());res.chuked(true);//Specifyacallbackfuctiowhesedig//sessio_ptr->sed(std::move(res));sessio_ptr->sed(std::move(res),[&res](std::size_tbytes_set){autoopeed=res.body().is_ope();std::igore=opeed;autoerr=asio2::get_last_error();std::igore=err;});//sessio_ptr->sed(std::move(res),asio::use_future);retur;}std::cout<<req<<std::edl;if(true){//用make_respose生成一个http响应对象,状态码200表示操作成功,"suceess"是HTTP消息的body部分内容autorep=http::make_respose(200,"suceess");sessio_ptr->sed(rep,[](){autoerr=asio2::get_last_error();std::igore=err;});}else{//也可以直接发送一个http标准响应字符串,内部会将这个字符串自动转换为http响应对象再发送出去std::strig_viewrep="HTTP/1.1404NotFoud\r\"\"Server:Boost.Beast/181\r\"\"Cotet-Legth:7\r\"\"\r\"\"failure";//testsedstrigsequece,thestrigwillautomaticallyparseditoastadardhttprequestsessio_ptr->sed(rep,[](std::size_tbytes_set){autoerr=asio2::get_last_error();std::igore=err;});}});server.start(host,port);客户端:asio2::error_codeec;autoreq1=http::make_request("https://www.baidu.com/get_user?ame=a");//通过URL字符串生成一个http请求对象autoreq2=http::make_request("GET/HTTP/1.1\r\Host:127.0.0.1:8443\r\\r\");//通过http协议字符串生成一个http请求对象req2.set(http::field::timeout,5000);//给请求设置一个超时时间autorep1=asio2::http_cliet::execute("https://www.baidu.com/get_user?ame=a",ec);//通过URL字符串直接请求某个网址,返回结果在rep1中,如果有错误,错误码保存在ec中autorep2=asio2::http_cliet::execute("127.0.0.1","8080",req2);//通过IP端口以及前面生成的req2请求对象来发送一个http请求std::cout<<rep2<<std::edl;//显示http请求结果std::strigstreamss;ss<<rep2;std::strigresult=ss.str();//通过这种方式将http请求结果转换为字符串

 

其它的HTTP使用方式以及WEBSOCKET使用方式请参考demo代码

 

ICMP:classpig_test//模拟在一个类对象中使用pig组件(其它所有如TCP/UDP/HTTP等组件一样可以在类对象中使用){asio2::pigpig;public:pig_test():pig(10)//构造函数传入的10表示只pig10次后就结束,传入-1表示一直pig{pig.timeout(std::chroo::secods(3));//设置pig超时pig.iterval(std::chroo::secods(1));//设置pig间隔pig.body("0123456789abcdefghijklmopqrstovuxyz");pig.bid_recv(&pig_test::o_recv,this)//绑定当前这个类的成员函数作为监听器.bid_start(std::bid(&pig_test::o_start,this,std::placeholders::_1))//也是绑定成员函数.bid_stop([this](asio::error_codeec){this->o_stop(ec);});//绑定lambda}voido_recv(asio2::icmp_rep&rep){if(rep.lag.cout()==-1)//如果延时的值等于-1表示超时了std::cout<<"requesttimedout"<<std::edl;elsestd::cout<<rep.total_legth()-rep.header_legth()<<"bytesfrom"<<rep.source_address()<<":icmp_seq="<<rep.sequece_umber()<<",ttl="<<rep.time_to_live()<<",time="<<std::chroo::duratio_cast<std::chroo::millisecods>(rep.lag).cout()<<"ms"<<std::edl;}voido_start(asio::error_codeec){pritf("start:%d%s\",asio2::last_error_val(),asio2::last_error_msg().c_str());}voido_stop(asio::error_codeec){pritf("stop:%d%s\",asio2::last_error_val(),asio2::last_error_msg().c_str());}voidru(){if(!pig.start("127.0.0.1"))//if(!pig.start("123.45.67.89"))//if(!pig.start("stackoverflow.com"))pritf("startfailure:%s\",asio2::last_error_msg().c_str());while(std::getchar()!='\');pig.stop();//pig结束后可以输出统计信息,包括丢包率,平均延时时长等pritf("lossrate:%.0lf%%averagetime:%lldms\",pig.plp(),std::chroo::duratio_cast<std::chroo::millisecods>(pig.avg_lag()).cout());}}; SSL: TCP/HTTP/WEBSOCKET均支持SSL功能(需要在cofig.hpp中将#defieASIO2_USE_SSL宏定义放开)asio2::tcps_serverserver;//从内存字符串加载SSL证书(具体请查看demo代码)server.set_cert("test",cer,key,dh);//cer,key,dh这三个字符串的定义请查看demo代码//从文件加载SSL证书//server.set_cert_file("test","server.crt","server.key","dh512.pem");

  

TCP/HTTP/WEBSOCKET服务端、客户端等SSL功能请到DEMO代码中查看。  串口:请查看demo示例代码serialport部分

 

其它:定时器//框架中提供了定时器功能,使用非常简单,如下:asio2::timertimer;//参数1表示定时器ID,参数2表示定时器间隔,参数3为定时器回调函数timer.start_timer(1,std::chroo::secods(1),[&](){pritf("timer1\");if(true)//满足某个条件时关闭定时器,当然也可以在其它任意地方关闭定时器timer.stop_timer(1);});

  

还有其它一些辅助类的功能,请在源码或使用中去体会吧.

 

功能介绍

开源基于asio的网络通信框架asio2,支持TCP,UDP,HTTP,RPC,SSL,跨平台,支持可靠UDP,支持TCP自动拆包,TCP数据报模式等 C++开发网络通信程序时用asio是个不错...

声明:本文仅代表作者观点,不代表本站立场。如果侵犯到您的合法权益,请联系我们删除侵权资源!如果遇到资源链接失效,请您通过评论或工单的方式通知管理员。未经允许,不得转载,本站所有资源文章禁止商业使用运营!
下载安装【程序员客栈】APP
实时对接需求、及时收发消息、丰富的开放项目需求、随时随地查看项目状态

评论