文章目录

  • 一.功能介绍
  • 二.string类型数据交互
    • 2.1 程序源码
    • 2.2 编译&&执行
    • 2.3 程序执行结果
  • 三.byte类型数据交互
    • 3.1 程序源码
    • 3.2 编译&&执行
    • 3.3 程序执行结果

一.功能介绍

  基于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"int main(int argc, char** argv)
{if(2 != argc){std::cout<<"Usage: "<<argv[0]<< " port"<<std::endl;return -1;}try {boost::asio::io_context io;int port = atoi(argv[1]);     // get server portsoftware::server(io, port);   // 开启一个server,ip地址为server主机地址,port为mian函数传入}catch (std::exception& e) {std::cout<<"main exception: " << e.what()<<std::endl;}return 0;
}

software.hpp

#ifndef __SOFTWARE_HPP__
#define __SOFTWARE_HPP__#include <string>
#include <iostream>
#include <boost/asio.hpp>namespace software {//! Session deadline durationconstexpr auto SESSION_TIMEOUT = std::chrono::minutes(2);//! Protocol delimiter to software client;分隔符:接收来自client的string数据必须以"}\n"结尾static constexpr char const* delimiter = "}";  namespace asio = boost::asio;using tcp      = asio::ip::tcp;/*** @brief   Session for software* Inherit @class enable_shared_from_this<>* in order to give the lifecycle to io context,* it'll causes the lifecycle automatically end when connection break* (async operation will return when connection break)* @code* asio::io_context io;* session sess(io, std::move(socket));* io.run();* @endcode*/class session{public:/* session constructor function */session(asio::io_context& io, tcp::socket socket);/* session destructor function */~session();private:/*! Async read session socket */void do_read();/*! Async wait deadline */void async_deadline_wait();/*! software on message handler */void on_message(std::string&& message);private:tcp::socket        socket_;      // tcp socketstd::string        recv_data_;   // recv buffer[string]asio::steady_timer deadline_;    // wait deadline time,expire it will disconnect auto };/*** @brief  Start server to software(同步方式accept)* Will serve client one by one(同步方式)* @param[in]   io      The asio io context* @param[in]   port    The listen port*/inline void server(asio::io_context& io, unsigned short port){std::cout<<"sync server start, listen port: " << port << std::endl;tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), port));// 一次处理一个连接[one by one]while (true) {using namespace std;// client请求放在队列中,循环逐个处理,处理完继续阻塞try{tcp::socket sock(io);acceptor.accept(sock);               // 一开始会阻塞在这,等待software client连接io.restart();  session sess(io, std::move(sock));   // io socketio.run();                            // run until session async operations done,调用run()函数进入io事件循环}catch (std::exception& e) {std::cout<<"boost server exception: " << e.what()<<std::endl;}}}
}  // namespace software
#endif

software.cpp

#include "software.hpp"using namespace std;namespace software {/*** @brief       Session construct function* @param[in]   io      The io context* @param[in]   socket  The connected session socket*/session::session(asio::io_context& io, tcp::socket socket): socket_(std::move(socket)), deadline_(io){std::cout<<"session created: " << socket_.remote_endpoint() <<std::endl;do_read();                //在构造函数中调用do_read()函数完成对software数据的读取async_deadline_wait();    //set on-request-deadline}session::~session(){std::cout<<"session destruct!" << std::endl;}/*** @brief   从software异步读取数据并存放在recv_data_中*/void session::do_read(){auto handler = [this](std::error_code ec, std::size_t length) {// recv data success, dispose the received data in [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();                     // Register async read operation again,重新执行读取操作}// error occured, shutdown the sessionelse if (socket_.is_open()) {std::cout<<"client offline, close session" << std::endl;socket_.shutdown(asio::socket_base::shutdown_both); // 关闭socketsocket_.close();                                    // 关闭socketdeadline_.cancel();                                 // deadline wait计时取消}};std::cout<<"server waiting message..." << std::endl;// block here until received the delimiterasio::async_read_until(socket_, asio::dynamic_buffer(recv_data_), delimiter,           // 读取终止条件(分隔符号)handler);            // 消息处理句柄函数deadline_.expires_after(SESSION_TIMEOUT);   // close session if no request,超时2min自动关闭session}/*** @brief   Async wait for the deadline,计时等待函数* @pre     @a deadline_.expires_xxx() must called*/void session::async_deadline_wait(){using namespace std::chrono;deadline_.async_wait( //! lambda function[this](std::error_code) {if (!socket_.is_open())return;if (deadline_.expiry() <= asio::steady_timer::clock_type::now()) {std::cout<< "client no data more than <" << duration_cast<milliseconds>(SESSION_TIMEOUT).count()<< "> ms, shutdown" << std::endl;socket_.shutdown(asio::socket_base::shutdown_both);socket_.close();return;}async_deadline_wait();}  );}/*** @brief       SOFTWARE on message handler* @param[in]   message The received message* &&表示右值引用,可以将字面常量、临时对象等右值绑定到右值引用上(也可以绑定到const 左值引用上,但是左值不能绑定到右值引用上)* 右值引用也可以看作起名,只是它起名的对象是一个将亡值。然后延续这个将亡值的生命,直到这个引用销毁的右值的生命也结束了。*/void session::on_message(std::string&& message){using namespace std;try {// print receive datastd::cout<<"recv from client is: "<<message<<std::endl;// response to clientstring send_buf = "hello client, you send data is: " + message;asio::write(socket_, asio::buffer(send_buf));}catch (exception& ex) {std::cout<<"some exception occured: "<< ex.what() << std::endl;}}
}  // namespace software

分析一下系统执行流程:

  1. 在main函数中传入io和port,调用 software.hpp中的server(asio::io_context& io, unsigned short port)函数。
  2. 在server()函数中while(True)循环体中accept来自client的连接,每次接收到一个client的连接会创建一个session对象,在session对象中处理本次的连接socket。注意,此处采用的是one by one的同步处理方式,只有上一个session处理完成才能处理下一个session的请求,但是同步发送的请求消息不会丢失,只是暂时不会处理和返回;总的来说,server会按照请求的顺序进行one by one处理
  3. session对象创建时会调用构造函数,其构造函数主要做了两件事情:一是调用do_read()函数进行等待读取来自client的数据并处理;二是通过async_deadline_wait()设置本次session连接的超时处理方法,超时时间默认设置为SESSION_TIMEOUT:deadline_.expires_after(SESSION_TIMEOUT)。
  4. 在do_read()函数中采用async_read_until()函数读取来自client的数据,async_read_until()函数会将传入的delimiter分隔符作为本次接收的结束标识。
  5. 当判断本次接收数据完成后,会调用handler句柄对消息进行处理,在handler句柄中主要做了两件事情:一是将收到的string信息传入到on_message()消息处理函数中进行处理,只有当本条消息处理完成后才能接收下一条消息并处理,消息会阻塞等待,但是不会丢失;二是在消息处理完成后再次调用do_read()函数,进入read_until()等待消息,如此循环
  6. 当发生错误或异常,在hander中会关闭本次socket连接,并且不会再调用其他循环体,表示本次session通信结束,之后调用析构函数析构session对象
      socket_.shutdown(asio::socket_base::shutdown_both); // 关闭socket
      socket_.close(); // 关闭socket
      deadline_.cancel(); // deadline wait计时取消
  7. 在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 程序执行结果

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

  可以看出,当超过2min没有收到来自clinet的消息,server会自动断开连接。
  tips:client1和clinet2可同时与server建立连接并发送数据,但是server会按照连接建立的先后顺序对client发送的请求进行one by one处理,比如clinet1先与server建立了连接,那么只有等到clinet1的所有请求执行完成才会处理client2发送的请求;在等待期间client2发送的请求不会处理,但不会丢失。

三.byte类型数据交互

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

  1. 将session类中的string recv_data_;替换成u_int8_t recv_data_[MAX_RECV_LEN];
  2. 数据读取方式由read_until()改为:socket_.async_receive(asio::buffer(recv_data_,MAX_RECV_LEN),handler);
  3. on_message()数据处理函数变为:void on_message(const u_int8_t* recv_buf,std::size_t recv_len);
  4. 数据发送方式变为:socket_.async_send(asio::buffer(recv_buf,recv_len),[](error_code ec, size_t size){});

3.1 程序源码

mian.cpp
  同上
software.hpp

#ifndef __SOFTWARE_HPP__
#define __SOFTWARE_HPP__#include <string>
#include <iostream>
#include <boost/asio.hpp>#define MAX_RECV_LEN 2048 namespace software {//! Session deadline durationconstexpr auto SESSION_TIMEOUT = std::chrono::minutes(2);//! Protocol delimiter to software client;分隔符:接收来自client的string数据必须以"}\n"结尾static constexpr char const* delimiter = "}";  namespace asio = boost::asio;using tcp      = asio::ip::tcp;/*** @brief   Session for software* Inherit @class enable_shared_from_this<>* in order to give the lifecycle to io context,* it'll causes the lifecycle automatically end when connection break* (async operation will return when connection break)* @code* asio::io_context io;* session sess(io, std::move(socket));* io.run();* @endcode*/class session{public:/* session constructor function */session(asio::io_context& io, tcp::socket socket);/* session destructor function */~session();private:/*! Async read session socket */void do_read();/*! Async wait deadline */void async_deadline_wait();/*! software on message handler */void on_message(const u_int8_t* recv_buf,std::size_t recv_len);private:tcp::socket socket_;                 // tcp socketu_int8_t recv_data_[MAX_RECV_LEN];   // recv buffer[byte]asio::steady_timer deadline_;        // wait deadline time,expire it will disconnect auto };/*** @brief  Start server to software(同步方式accept)* Will serve client one by one(同步方式)* @param[in]   io      The asio io context* @param[in]   port    The listen port*/inline void server(asio::io_context& io, unsigned short port){std::cout<<"sync server start, listen port: " << port << std::endl;tcp::acceptor acceptor(io, tcp::endpoint(tcp::v4(), port));// 一次处理一个连接[one by one]while (true) {using namespace std;// client请求放在队列中,循环逐个处理,处理完继续阻塞try{tcp::socket sock(io);acceptor.accept(sock);               // 一开始会阻塞在这,等待software client连接io.restart();  session sess(io, std::move(sock));   // io socketio.run();                            // run until session async operations done,调用run()函数进入io事件循环}catch (std::exception& e) {std::cout<<"boost server exception: " << e.what()<<std::endl;}}}
}  // namespace software
#endif

software.cpp

#include "software.hpp"using namespace std;namespace software {/*** @brief       Session construct function* @param[in]   io      The io context* @param[in]   socket  The connected session socket*/session::session(asio::io_context& io, tcp::socket socket): socket_(std::move(socket)), deadline_(io){std::cout<<"session created: " << socket_.remote_endpoint() <<std::endl;do_read();                //在构造函数中调用do_read()函数完成对software数据的读取async_deadline_wait();    //set on-request-deadline}session::~session(){std::cout<<"session destruct!" << std::endl;}/*** @brief   从software异步读取数据并存放在recv_data_中*/void session::do_read(){auto handler = [this](std::error_code ec, std::size_t length) {// recv data success, dispose the received data in [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();                     // Register async read operation again,重新执行读取操作}// error occured, shutdown the sessionelse if (socket_.is_open()) {std::cout<<"client offline, close session" << std::endl;socket_.shutdown(asio::socket_base::shutdown_both); // 关闭socketsocket_.close();                                    // 关闭socketdeadline_.cancel();                                 // deadline wait计时取消}};std::cout<<"server waiting message..." << std::endl;//block here to receive some byte from clientsocket_.async_receive(asio::buffer(recv_data_,MAX_RECV_LEN),handler);deadline_.expires_after(SESSION_TIMEOUT);   // close session if no request,超时2min自动关闭session}/*** @brief   Async wait for the deadline,计时等待函数* @pre     @a deadline_.expires_xxx() must called*/void session::async_deadline_wait(){using namespace std::chrono;deadline_.async_wait( //! lambda function[this](std::error_code) {if (!socket_.is_open())return;if (deadline_.expiry() <= asio::steady_timer::clock_type::now()) {std::cout<< "client no data more than <" << duration_cast<milliseconds>(SESSION_TIMEOUT).count()<< "> ms, shutdown" << std::endl;socket_.shutdown(asio::socket_base::shutdown_both);socket_.close();return;}async_deadline_wait();}  );}/*** @brief       SOFTWARE on message handler* @param[in]   recv_buf The received byte array address* @param[in]   recv_len The received byte length*/void session::on_message(const u_int8_t* recv_buf,std::size_t recv_len){using namespace std;try {// print receive datastd::cout<<"recv data length is: "<<recv_len<<"   data is: ";for(int i = 0; i<recv_len; i++)printf("%x ",recv_buf[i]);std::cout<<std::endl;// response to clientsocket_.async_send(asio::buffer(recv_buf,recv_len),[](error_code ec, size_t size){});}catch (exception& ex) {std::cout<<"some exception occured: "<< ex.what() << std::endl;}}
}  // namespace software

3.2 编译&&执行

编译:g++ main.cpp software.cpp -o iotest -lpthread -lboost_system -std=c++17
执行:./iotest 11112 (监听端口为11112)

3.3 程序执行结果

基于boost asio实现sync tcp server通信相关推荐

  1. Boost:基于boost::asio的延迟tcp服务器测试程序

    Boost:基于boost::asio的延迟tcp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟tcp服务器测 ...

  2. 基于boost asio实现的支持ssl的通用socket框架

    情景分析    现已存在一个可用稳定的异步客户端类http_client_base,该类基于boost asio实现了连接服务器,发送请求,获取响应和解析http数据等操作,该类的大致实现框架如下   ...

  3. boost::asio编程-同步TCP

    boost.asio库是一个跨平台的网络及底层IO的C++编程库,它使用现代C++手法实现了统一的异步调用模型. boost.asio库支持TCP.UDP.ICMP通信协议. 下面介绍同步TCP模式: ...

  4. c++语言 tcp例子,C++ boost::asio编程-同步TCP详解及实例代码

    boost::asio编程-同步TCP boost.asio库是一个跨平台的网络及底层IO的C++编程库,它使用现代C++手法实现了统一的异步调用模型. boost.asio库支持TCP.UDP.IC ...

  5. Boost:基于boost::asio的延迟udp服务器测试程序

    Boost:基于boost::asio的延迟udp服务器测试程序 实现功能 C++实现代码 客户端源码 服务端源码 实现功能 boost::asio模块,基于boost::asio的延迟udp服务器测 ...

  6. Boost:基于boost::asio单元测试的测试程序

    Boost:基于boost::asio单元测试的测试程序 实现功能 C++实现代码 实现功能 boost::asio模块,基于boost::asio单元测试的测试程序 C++实现代码 #ifndef ...

  7. Boost:基于boost::asio模块引用计数程序

    Boost:基于boost::asio模块引用计数程序 实现功能 C++实现代码 实现功能 基于boost::asio模块引用计数程序 C++实现代码 #include <boost/asio. ...

  8. boost asio 异步实现tcp通讯

    一.前言 boost asio可算是一个简单易用,功能又强大可跨平台的C++通讯库,效率也表现的不错,linux环境是epoll实现的,而windows环境是iocp实现的.而tcp通讯是项目当中经常 ...

  9. boost.asio异步并发Tcp服务器

    简介 boost的asio采用的是Proactor模型,该模型的核心思想就是异步IO,IO在事件循环中,每个异步IO都绑定对应的回调函数,当IO完成后,对应的回调函数会在事件循环中执行. 异步并发模型 ...

最新文章

  1. Centos7上安装oracle11g/12c的安装教程推荐及注意事项
  2. Elixir 1.2带来多项功能增强和性能提升
  3. 4行代码再现《黑客帝国》数字雨,在终端实现的那种
  4. Java基础05 实施接口
  5. 关于控件的AutoSize属性影响界面布局的问题解决
  6. Java虚拟机学习 - 体系结构 内存模型(转载)
  7. 关于印发厦门市创建创业型城市政策支持体系的通知
  8. default value mysql_Mysql Field * doesn't have a default value解决方法
  9. 阿里云服务器学生计划--免费领取两个月
  10. MATLAB版 代码狗屁不通的狗屁不通文章生成器
  11. PS 滤镜算法原理 ——马赛克
  12. Arduino火焰传感器(红外线接收器)的使用
  13. python :脚本运行出现语法错误:IndentationError:unexpected indent(缩进问题)
  14. BUUCTF rsarsa
  15. 迭代器(list迭代器的实现)
  16. python猜单词游戏_python实现猜单词游戏
  17. Prege(图计算框架)l: A System for Large-Scale Graph Processing(译)
  18. Cadence设置——约束实现差分线动态等长
  19. java安装jre_Java Runtime Environment怎么安装 JRE安装详细图文教程
  20. 武器装备自动测试(ATE)系统设计要点

热门文章

  1. 深圳内推 | 粤港澳大湾区数字经济研究院招聘NLP/多模态算法实习生
  2. 【微信聊天记录制作词云】超详细保姆级教学!!!(详细步骤+代码)
  3. 什么是偏置电路,为什么需要偏置电路?
  4. FPGA和CY7C68013A的连接以及控制程序(1)
  5. 一个男人的日记片断,笑翻了
  6. 史上最全的mime-type大全
  7. java 翻转单词_单词翻转 - SegmentFault 思否
  8. css 阴影 效果_CSS阴影效果
  9. 将本地项目传到coding(gitHub)上
  10. 实现mnist手写数字识别(第一周)