C++基于boost asio如何实现sync tcp server通信(asio,boost,C++,开发技术)

时间:2024-05-09 09:42:22 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

这篇“C++基于boostasio如何实现synctcpserver通信”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C++基于boostasio如何实现synctcpserver通信”文章吧。

一.功能介绍

基于boost asio实现server端通信,采用one by one的同步处理方式,并且设置连接等待超时。下面给出了string和byte两种数据类型的通信方式,可覆盖基本通信场景需求。

二.string类型数据交互

  规定server与client双方交互的数据格式是string,并且server采用read_until的方式接收来自client的消息,通过delimiter(分隔符)来判断一帧数据接收完成,当未收到来自client的delimiter,那么server会一直等待,直到收到delimiter或超时。此处设置了本次回话的连接超时时间SESSION_TIMEOUT,程序中定义为2min,每次收到数据后重新计时,若是连续2min中内有收到来自client的任何消息,那么server会自动断开本次连接,并且析构本次的session。

  通过string发送和接收的数据采用ASCII码的编码方式,因此不能直接发送byte数据,不然会产生乱码(第三部分为byte数据交互);采用string数据传输方式可以方便的进行序列化与反序列化,例如采用json对象的传输的方式,可以方便的组织交互协议。

  下面是功能实现的完整程序,程序编译的前提是已经安装了boost库,boost库的安装及使用方法在我的前述博客已有提到:boost库安装及使用

2.1 程序源码

mian.cpp

#include"software.hpp"intmain(intargc,char**argv){if(2!=argc){std::cout<<"Usage:"<<argv[0]<<"port"<<std::endl;return-1;}try{boost::asio::io_contextio;intport=atoi(argv[1]);//getserverportsoftware::server(io,port);//开启一个server,ip地址为server主机地址,port为mian函数传入}catch(std::exception&e){std::cout<<"mainexception:"<<e.what()<<std::endl;}return0;}

software.hpp

#ifndef__SOFTWARE_HPP__#define__SOFTWARE_HPP__#include<string>#include<iostream>#include<boost/asio.hpp>namespacesoftware{//!SessiondeadlinedurationconstexprautoSESSION_TIMEOUT=std::chrono::minutes(2);//!Protocoldelimitertosoftwareclient;分隔符:接收来自client的string数据必须以"}\n"结尾staticconstexprcharconst*delimiter="}";namespaceasio=boost::asio;usingtcp=asio::ip::tcp;/***@briefSessionforsoftware*Inherit@classenable_shared_from_this<>*inordertogivethelifecycletoiocontext,*it'llcausesthelifecycleautomaticallyendwhenconnectionbreak*(asyncoperationwillreturnwhenconnectionbreak)*@code*asio::io_contextio;*sessionsess(io,std::move(socket));*io.run();*@endcode*/classsession{public:/*sessionconstructorfunction*/session(asio::io_context&io,tcp::socketsocket);/*sessiondestructorfunction*/~session();private:/*!Asyncreadsessionsocket*/voiddo_read();/*!Asyncwaitdeadline*/voidasync_deadline_wait();/*!softwareonmessagehandler*/voidon_message(std::string&&message);private:tcp::socketsocket_;//tcpsocketstd::stringrecv_data_;//recvbuffer[string]asio::steady_timerdeadline_;//waitdeadlinetime,expireitwilldisconnectauto};/***@briefStartservertosoftware(同步方式accept)*Willserveclientonebyone(同步方式)*@param[in]ioTheasioiocontext*@param[in]portThelistenport*/inlinevoidserver(asio::io_context&io,unsignedshortport){std::cout<<"syncserverstart,listenport:"<<port<<std::endl;tcp::acceptoracceptor(io,tcp::endpoint(tcp::v4(),port));//一次处理一个连接[onebyone]while(true){usingnamespacestd;//client请求放在队列中,循环逐个处理,处理完继续阻塞tcp::socketsock(io);acceptor.accept(sock);//一开始会阻塞在这,等待softwareclient连接io.restart();sessionsess(io,std::move(sock));//iosocketio.run();//rununtilsessionasyncoperationsdone,调用run()函数进入io事件循环}}}//namespacesoftware#endif

software.cpp

#include"software.hpp"usingnamespacestd;namespacesoftware{/***@briefSessionconstructfunction*@param[in]ioTheiocontext*@param[in]socketTheconnectedsessionsocket*/session::session(asio::io_context&io,tcp::socketsocket):socket_(std::move(socket)),deadline_(io){std::cout<<"sessioncreated:"<<socket_.remote_endpoint()<<std::endl;do_read();//在构造函数中调用do_read()函数完成对software数据的读取async_deadline_wait();//seton-request-deadline}session::~session(){std::cout<<"sessiondestruct!"<<std::endl;}/***@brief从software异步读取数据并存放在recv_data_中*/voidsession::do_read(){autohandler=[this](std::error_codeec,std::size_tlength){//recvdatasuccess,disposethereceiveddatain[on_message]funcif(!ec&&socket_.is_open()&&length!=0){on_message(recv_data_.substr(0,length));recv_data_.erase(0,length);//将recv_data_擦除为0do_read();//Registerasyncreadoperationagain,重新执行读取操作}//erroroccured,shutdownthesessionelseif(socket_.is_open()){std::cout<<"clientoffline,closesession"<<std::endl;socket_.shutdown(asio::socket_base::shutdown_both);//关闭socketsocket_.close();//关闭socketdeadline_.cancel();//deadlinewait计时取消}};std::cout<<"serverwaitingmessage..."<<std::endl;//blockhereuntilreceivedthedelimiterasio::async_read_until(socket_,asio::dynamic_buffer(recv_data_),delimiter,//读取终止条件(分隔符号)handler);//消息处理句柄函数deadline_.expires_after(SESSION_TIMEOUT);//closesessionifnorequest,超时2min自动关闭session}/***@briefAsyncwaitforthedeadline,计时等待函数*@pre@adeadline_.expires_xxx()mustcalled*/voidsession::async_deadline_wait(){usingnamespacestd::chrono;deadline_.async_wait(//!lambdafunction[this](std::error_code){if(!socket_.is_open())return;if(deadline_.expiry()<=asio::steady_timer::clock_type::now()){std::cout<<"clientnodatamorethan<"<<duration_cast<milliseconds>(SESSION_TIMEOUT).count()<<">ms,shutdown"<<std::endl;socket_.shutdown(asio::socket_base::shutdown_both);socket_.close();return;}async_deadline_wait();});}/***@briefSOFTWAREonmessagehandler*@param[in]messageThereceivedmessage*&&表示右值引用,可以将字面常量、临时对象等右值绑定到右值引用上(也可以绑定到const左值引用上,但是左值不能绑定到右值引用上)*右值引用也可以看作起名,只是它起名的对象是一个将亡值。然后延续这个将亡值的生命,直到这个引用销毁的右值的生命也结束了。*/voidsession::on_message(std::string&&message){usingnamespacestd;try{//printreceivedatastd::cout<<"recvfromclientis:"<<message<<std::endl;//responsetoclientstringsend_buf="helloclient,yousenddatais:"+message;asio::write(socket_,asio::buffer(send_buf));}catch(exception&ex){std::cout<<"someexceptionoccured:"<<ex.what()<<std::endl;}}}//namespacesoftware

分析一下系统执行流程:

  • 在main函数中传入io和port,调用 software.hpp中的server(asio::io_context& io, unsigned short port)函数。

  • 在server()函数中while(True)循环体中accept来自client的连接,每次接收到一个client的连接会创建一个session对象,在session对象中处理本次的连接socket。注意,此处采用的是one by one的同步处理方式,只有上一个session处理完成才能处理下一个session的请求,但是同步发送的请求消息不会丢失,只是暂时不会处理和返回;总的来说,server会按照请求的顺序进行one by one处理。

  • session对象创建时会调用构造函数,其构造函数主要做了两件事情:一是调用do_read()函数进行等待读取来自client的数据并处理;二是通过async_deadline_wait()设置本次session连接的超时处理方法,超时时间默认设置为SESSION_TIMEOUT:deadline_.expires_after(SESSION_TIMEOUT)。

  • 在do_read()函数中采用async_read_until()函数读取来自client的数据,async_read_until()函数会将传入的delimiter分隔符作为本次接收的结束标识。

  • 当判断本次接收数据完成后,会调用handler句柄对消息进行处理,在handler句柄中主要做了两件事情:一是将收到的string信息传入到on_message()消息处理函数中进行处理,只有当本条消息处理完成后才能接收下一条消息并处理,消息会阻塞等待,但是不会丢失;二是在消息处理完成后再次调用do_read()函数,进入read_until()等待消息,如此循环&hellip;

  • 当发生错误或异常,在hander中会关闭本次socket连接,并且不会再调用其他循环体,表示本次session通信结束,之后调用析构函数析构session对象。

&emsp;&emsp;socket_.shutdown(asio::socket_base::shutdown_both); // 关闭socket

&emsp;&emsp;socket_.close(); // 关闭socket

&emsp;&emsp;deadline_.cancel(); // deadline wait计时取消

  • 在on_message()消息处理函数中会对收到的string数据进行处理(上述程序中以打印代替),然后调用asio::write(socket_, asio::buffer(send_buf))将response发送给client。

2.2 编译&&执行

编译:g++ main.cpp software.cpp -o iotest -lpthread -lboost_system -std=c++17

执行:./iotest 11112 (监听端口为11112)

2.3 程序执行结果

C++基于boost asio如何实现sync tcp server通信

可以看出,client发送的每条消息都要以"}"结束,这是设定的delimter分隔符。

C++基于boost asio如何实现sync tcp server通信

可以看出,当超过2min没有收到来自clinet的消息,server会自动断开连接。

&emsp;&emsp;tips:client1和clinet2可同时与server建立连接并发送数据,但是server会按照连接建立的先后顺序对client发送的请求进行one by one处理,比如clinet1先与server建立了连接,那么只有等到clinet1的所有请求执行完成才会处理client2发送的请求;在等待期间client2发送的请求不会处理,但不会丢失。

三.byte类型数据交互

&emsp;&emsp;上述给出了string类型数据的交互,但是string类型的数据只能采用ASCII码的方式传输,在某些场景中,例如传感器,需要交互byte类型的数据。因此下面给出了byte[hex]类型数据的交互。与上述的string数据交互流程基本一致,几点区别在下面阐述:

  • 将session类中的string recv_data_;替换成u_int8_t recv_data_[MAX_RECV_LEN];

  • 数据读取方式由read_until()改为:socket_.async_receive(asio::buffer(recv_data_,MAX_RECV_LEN),handler);

  • on_message()数据处理函数变为:void on_message(const u_int8_t* recv_buf,std::size_t recv_len);

  • 数据发送方式变为:socket_.async_send(asio::buffer(recv_buf,recv_len),[](error_code ec, size_t size){});

3.1 程序源码

mian.cpp

&emsp;&emsp;同上

software.hpp

#ifndef__SOFTWARE_HPP__#define__SOFTWARE_HPP__#include<string>#include<iostream>#include<boost/asio.hpp>#defineMAX_RECV_LEN2048namespacesoftware{//!SessiondeadlinedurationconstexprautoSESSION_TIMEOUT=std::chrono::minutes(2);//!Protocoldelimitertosoftwareclient;分隔符:接收来自client的string数据必须以"}\n"结尾staticconstexprcharconst*delimiter="}";namespaceasio=boost::asio;usingtcp=asio::ip::tcp;/***@briefSessionforsoftware*Inherit@classenable_shared_from_this<>*inordertogivethelifecycletoiocontext,*it'llcausesthelifecycleautomaticallyendwhenconnectionbreak*(asyncoperationwillreturnwhenconnectionbreak)*@code*asio::io_contextio;*sessionsess(io,std::move(socket));*io.run();*@endcode*/classsession{public:/*sessionconstructorfunction*/session(asio::io_context&io,tcp::socketsocket);/*sessiondestructorfunction*/~session();private:/*!Asyncreadsessionsocket*/voiddo_read();/*!Asyncwaitdeadline*/voidasync_deadline_wait();/*!softwareonmessagehandler*/voidon_message(constu_int8_t*recv_buf,std::size_trecv_len);private:tcp::socketsocket_;//tcpsocketu_int8_trecv_data_[MAX_RECV_LEN];//recvbuffer[byte]asio::steady_timerdeadline_;//waitdeadlinetime,expireitwilldisconnectauto};/***@briefStartservertosoftware(同步方式accept)*Willserveclientonebyone(同步方式)*@param[in]ioTheasioiocontext*@param[in]portThelistenport*/inlinevoidserver(asio::io_context&io,unsignedshortport){std::cout<<"syncserverstart,listenport:"<<port<<std::endl;tcp::acceptoracceptor(io,tcp::endpoint(tcp::v4(),port));//一次处理一个连接[onebyone]while(true){usingnamespacestd;//client请求放在队列中,循环逐个处理,处理完继续阻塞tcp::socketsock(io);acceptor.accept(sock);//一开始会阻塞在这,等待softwareclient连接io.restart();sessionsess(io,std::move(sock));//iosocketio.run();//rununtilsessionasyncoperationsdone,调用run()函数进入io事件循环}}}//namespacesoftware#endif

software.cpp

#include"software.hpp"usingnamespacestd;namespacesoftware{/***@briefSessionconstructfunction*@param[in]ioTheiocontext*@param[in]socketTheconnectedsessionsocket*/session::session(asio::io_context&io,tcp::socketsocket):socket_(std::move(socket)),deadline_(io){std::cout<<"sessioncreated:"<<socket_.remote_endpoint()<<std::endl;do_read();//在构造函数中调用do_read()函数完成对software数据的读取async_deadline_wait();//seton-request-deadline}session::~session(){std::cout<<"sessiondestruct!"<<std::endl;}/***@brief从software异步读取数据并存放在recv_data_中*/voidsession::do_read(){autohandler=[this](std::error_codeec,std::size_tlength){//recvdatasuccess,disposethereceiveddatain[on_message]funcif(!ec&&socket_.is_open()&&length!=0){on_message(recv_data_,length);memset(recv_data_,0,sizeof(recv_data_));//将recv_data_擦除为0do_read();//Registerasyncreadoperationagain,重新执行读取操作}//erroroccured,shutdownthesessionelseif(socket_.is_open()){std::cout<<"clientoffline,closesession"<<std::endl;socket_.shutdown(asio::socket_base::shutdown_both);//关闭socketsocket_.close();//关闭socketdeadline_.cancel();//deadlinewait计时取消}};std::cout<<"serverwaitingmessage..."<<std::endl;//blockheretoreceivesomebytefromclientsocket_.async_receive(asio::buffer(recv_data_,MAX_RECV_LEN),handler);deadline_.expires_after(SESSION_TIMEOUT);//closesessionifnorequest,超时2min自动关闭session}/***@briefAsyncwaitforthedeadline,计时等待函数*@pre@adeadline_.expires_xxx()mustcalled*/voidsession::async_deadline_wait(){usingnamespacestd::chrono;deadline_.async_wait(//!lambdafunction[this](std::error_code){if(!socket_.is_open())return;if(deadline_.expiry()<=asio::steady_timer::clock_type::now()){std::cout<<"clientnodatamorethan<"<<duration_cast<milliseconds>(SESSION_TIMEOUT).count()<<">ms,shutdown"<<std::endl;socket_.shutdown(asio::socket_base::shutdown_both);socket_.close();return;}async_deadline_wait();});}/***@briefSOFTWAREonmessagehandler*@param[in]recv_bufThereceivedbytearrayaddress*@param[in]recv_lenThereceivedbytelength*/voidsession::on_message(constu_int8_t*recv_buf,std::size_trecv_len){usingnamespacestd;try{//printreceivedatastd::cout<<"recvdatalengthis:"<<recv_len<<"datais:";for(inti=0;i<recv_len;i++)printf("%x",recv_buf[i]);std::cout<<std::endl;//responsetoclientsocket_.async_send(asio::buffer(recv_buf,recv_len),[](error_codeec,size_tsize){});}catch(exception&ex){std::cout<<"someexceptionoccured:"<<ex.what()<<std::endl;}}}//namespacesoftware

3.2 编译&&执行

编译:g++ main.cpp software.cpp -o iotest -lpthread -lboost_system -std=c++17

执行:./iotest 11112 (监听端口为11112)

3.3 程序执行结果

C++基于boost asio如何实现sync tcp server通信

以上就是关于“C++基于boostasio如何实现synctcpserver通信”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。

本文:C++基于boost asio如何实现sync tcp server通信的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:怎么使用vue3+Pinia+TypeScript实现封装轮播图组件下一篇:

16 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18