boost asio 异步实现tcp通讯

摘要:
而tcp通讯是项目当中经常用到通讯方式之一,实现的方法有各式各样,因此总结一套适用于自己项目的方法是很有必要,很可能下一个项目直接套上去就可以用了。#pragmaonce#include#include#include#include#include#include#include#include#include#include#include#include#includeusingboost::asio::ip::tcp;namespacefirebird{enumcommand{heartbeat=0,regist,normal};conststd::stringtag="KDS";classFIREBIRD_DECLsocket_session;typedefboost::shared_ptrsocket_session_ptr;classFIREBIRD_DECLsocket_session:publicboost::enable_shared_from_this,privateboost::noncopyable{public:typedefboost::functionclose_callback;typedefboost::functionread_data_callback;socket_session;~socket_session;DWORDid(){returnm_id;}WORDget_business_type(){returnm_business_type;}voidset_business_type{m_business_type=type;}DWORDget_app_id(){returnm_app_id;}voidset_app_id{m_app_id=app_id;}std::string&get_remote_addr(){returnm_name;}voidset_remote_addr{m_name=name;}tcp::socket&socket(){returnm_socket;}voidinstallCloseCallBack{close_cb=cb;}voidinstallReadDataCallBack{read_data_cb=cb;}voidstart();voidclose();voidasync_write;voidasync_write;boolis_timeout();voidset_op_time(){std::time;}private:staticboost::detail::atomic_countm_last_id;DWORDm_id;WORDm_business_type;DWORDm_app_id;std::stringm_name;boost::arraysHeader;std::stringsBody;tcp::socketm_socket;boost::asio::io_service&m_io_service;std::time_tm_last_op_time;close_callbackclose_cb;read_data_callbackread_data_cb;//发送消息voidhandle_write;//读消息头voidhandle_read_header;//读消息体voidhandle_read_body;voidhandle_close();};}这里注意的是,定义了一个tag="KDS",目的是为了检查收到的数据包是否有效,每一个数据包前3个字节不为“KDS”,那么就认为是非法的请求包,你也可以定义tag等于其它字符串,只要按协议发包就正常,当然这是比较简单的数据包检查方法了。

---恢复内容开始---

asioboost

目录(?)[-]

  1. 一前言
  2. 二实现思路
    1. 通讯包数据结构
    2. 连接对象
    3. 连接管理器
    4. 服务器端的实现
    5. 对象串行化
一、前言

boost asio可算是一个简单易用,功能又强大可跨平台的C++通讯库,效率也表现的不错,linux环境是epoll实现的,而windows环境是iocp实现的。而tcp通讯是项目当中经常用到通讯方式之一,实现的方法有各式各样,因此总结一套适用于自己项目的方法是很有必要,很可能下一个项目直接套上去就可以用了。

二、实现思路

1.通讯包数据结构

boost asio 异步实现tcp通讯第1张

Tag:检查数据包是否合法,具体会在下面讲解;

Length:描述Body的长度;

Command:表示数据包的类型,0表示心跳包(长连接需要心跳来检测连接是否正常),1表示注册包(客户端连接上服务器之后要将相关信息注册给服务器),2表示业务消息包;

business_type:业务消息包类型,服务器会根据业务消息包类型将数据路由到对应的客户端(客户端是有业务类型分类的);

app_id:客户端唯一标识符;

Data:消息数据;

2.连接对象

客户端连接上服务器之后,双方都会产生一个socket连接对象,通过这个对象可以收发数据,因此我定义为socket_session。

//socket_session.h

  1. #pragmaonce
  2. #include<iostream>
  3. #include<list>
  4. #include<hash_map>
  5. #include<boost/bind.hpp>
  6. #include<boost/asio.hpp>
  7. #include<boost/shared_ptr.hpp>
  8. #include<boost/make_shared.hpp>
  9. #include<boost/thread.hpp>
  10. #include<boost/thread/mutex.hpp>
  11. #include<boost/enable_shared_from_this.hpp>
  12. #include<firebird/log/logger_log4.hpp>
  13. #include<firebird/detail/config.hpp>
  14. #include<firebird/socket_utils/message_archive.hpp>
  15. usingboost::asio::ip::tcp;
  16. namespacefirebird{
  17. enumcommand{heartbeat=0,regist,normal};
  18. conststd::stringtag="KDS";
  19. classFIREBIRD_DECLsocket_session;
  20. typedefboost::shared_ptr<socket_session>socket_session_ptr;
  21. classFIREBIRD_DECLsocket_session:
  22. publicboost::enable_shared_from_this<socket_session>,
  23. privateboost::noncopyable
  24. {
  25. public:
  26. typedefboost::function<void(socket_session_ptr)>close_callback;
  27. typedefboost::function<void(
  28. constboost::system::error_code&,
  29. socket_session_ptr,message&)>read_data_callback;
  30. socket_session(boost::asio::io_service&io_service);
  31. ~socket_session(void);
  32. DWORDid(){returnm_id;}
  33. WORDget_business_type(){returnm_business_type;}
  34. voidset_business_type(WORDtype){m_business_type=type;}
  35. DWORDget_app_id(){returnm_app_id;}
  36. voidset_app_id(DWORDapp_id){m_app_id=app_id;}
  37. std::string&get_remote_addr(){returnm_name;}
  38. voidset_remote_addr(std::string&name){m_name=name;}
  39. tcp::socket&socket(){returnm_socket;}
  40. voidinstallCloseCallBack(close_callbackcb){close_cb=cb;}
  41. voidinstallReadDataCallBack(read_data_callbackcb){read_data_cb=cb;}
  42. voidstart();
  43. voidclose();
  44. voidasync_write(conststd::string&sMsg);
  45. voidasync_write(message&msg);
  46. boolis_timeout();
  47. voidset_op_time(){std::time(&m_last_op_time);}
  48. private:
  49. staticboost::detail::atomic_countm_last_id;
  50. DWORDm_id;
  51. WORDm_business_type;
  52. DWORDm_app_id;
  53. std::stringm_name;
  54. boost::array<char,7>sHeader;
  55. std::stringsBody;
  56. tcp::socketm_socket;
  57. boost::asio::io_service&m_io_service;
  58. std::time_tm_last_op_time;
  59. close_callbackclose_cb;
  60. read_data_callbackread_data_cb;
  61. //发送消息
  62. voidhandle_write(constboost::system::error_code&e,
  63. std::size_tbytes_transferred,std::string*pmsg);
  64. //读消息头
  65. voidhandle_read_header(constboost::system::error_code&error);
  66. //读消息体
  67. voidhandle_read_body(constboost::system::error_code&error);
  68. voidhandle_close();
  69. };
  70. }

这里注意的是,定义了一个tag="KDS",目的是为了检查收到的数据包是否有效,每一个数据包前3个字节不为“KDS”,那么就认为是非法的请求包,你也可以定义tag等于其它字符串,只要按协议发包就正常,当然这是比较简单的数据包检查方法了。比较严谨的方法是双方使用哈希算法来检查的,怎么做,这里先不做详解。

//socket_session.cpp

  1. #include"socket_session.h"
  2. namespacefirebird{
  3. boost::detail::atomic_countsocket_session::m_last_id(0);
  4. socket_session::socket_session(boost::asio::io_service&io_srv)
  5. :m_io_service(io_srv),m_socket(io_srv),
  6. m_business_type(0),m_app_id(0)
  7. {
  8. m_id=++socket_session::m_last_id;
  9. }
  10. socket_session::~socket_session(void)
  11. {
  12. m_socket.close();
  13. }
  14. voidsocket_session::start()
  15. {
  16. m_socket.set_option(boost::asio::ip::tcp::acceptor::linger(true,0));
  17. m_socket.set_option(boost::asio::socket_base::keep_alive(true));
  18. std::time(&m_last_op_time);
  19. constboost::system::error_codeerror;
  20. handle_read_header(error);
  21. }
  22. voidsocket_session::handle_close()
  23. {
  24. try{
  25. m_socket.close();
  26. close_cb(shared_from_this());
  27. }
  28. catch(std::exception&e)
  29. {
  30. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<e.what()<<"]");
  31. }
  32. catch(...)
  33. {
  34. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:[未知异常]");
  35. }
  36. }
  37. voidsocket_session::close()
  38. {
  39. //由于回调中有加锁的情况,必须提交到另外一个线程去做,不然会出现死锁
  40. m_io_service.post(boost::bind(&socket_session::handle_close,shared_from_this()));
  41. }
  42. staticintconnection_timeout=60;
  43. boolsocket_session::is_timeout()
  44. {
  45. std::time_tnow;
  46. std::time(&now);
  47. returnnow-m_last_op_time>connection_timeout;
  48. }
  49. //读消息头
  50. voidsocket_session::handle_read_header(constboost::system::error_code&error)
  51. {
  52. LOG4CXX_DEBUG(firebird_log,KDS_CODE_INFO<<"enter.");
  53. try{
  54. if(error)
  55. {
  56. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<error.message().c_str()<<"]");
  57. close();
  58. return;
  59. }
  60. std::stringdata;
  61. data.swap(sBody);
  62. boost::asio::async_read(m_socket,
  63. boost::asio::buffer(sHeader),
  64. boost::bind(&socket_session::handle_read_body,shared_from_this(),
  65. boost::asio::placeholders::error));
  66. if(data.length()>0&&data!="")
  67. {//读到数据回调注册的READ_DATA函数
  68. messagemsg;
  69. message_iarchive(msg,data);
  70. read_data_cb(error,shared_from_this(),msg);
  71. }
  72. }
  73. catch(std::exception&e)
  74. {
  75. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<e.what()<<"]");
  76. close();
  77. }
  78. catch(...)
  79. {
  80. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:[未知异常]");
  81. close();
  82. }
  83. }
  84. //读消息体
  85. voidsocket_session::handle_read_body(constboost::system::error_code&error)
  86. {
  87. LOG4CXX_DEBUG(firebird_log,KDS_CODE_INFO<<"enter.");
  88. try{
  89. if(error)
  90. {
  91. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<error.message().c_str()<<"]");
  92. close();
  93. return;
  94. }
  95. if(tag.compare(0,tag.length(),sHeader.data(),0,tag.length()))
  96. {
  97. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:[这是个非法连接!]");
  98. close();
  99. return;
  100. }
  101. DWORDdwLength=0;
  102. char*len=(char*)&dwLength;
  103. memcpy(len,&sHeader[tag.length()],sizeof(dwLength));
  104. sBody.resize(dwLength);
  105. char*pBody=&sBody[0];
  106. boost::asio::async_read(m_socket,
  107. boost::asio::buffer(pBody,dwLength),
  108. boost::bind(&socket_session::handle_read_header,shared_from_this(),
  109. boost::asio::placeholders::error));
  110. }
  111. catch(std::exception&e)
  112. {
  113. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<e.what()<<"]");
  114. close();
  115. }
  116. catch(...)
  117. {
  118. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:[未知异常]");
  119. close();
  120. }
  121. }
  122. voidsocket_session::handle_write(constboost::system::error_code&error,
  123. std::size_tbytes_transferred,std::string*pmsg)
  124. {
  125. //数据发送成功就销毁
  126. if(pmsg!=NULL)
  127. {
  128. deletepmsg;
  129. }
  130. if(error)
  131. {
  132. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<error.message().c_str()<<"]");
  133. close();
  134. return;
  135. }
  136. }
  137. voidsocket_session::async_write(conststd::string&sMsg)
  138. {
  139. LOG4CXX_DEBUG(firebird_log,KDS_CODE_INFO<<"enter.")
  140. try
  141. {
  142. DWORDdwLength=sMsg.size();
  143. char*pLen=(char*)&dwLength;
  144. //由于是异步发送,要保证数据发送完整时,才把数据销毁
  145. std::string*msg=newstd::string();
  146. msg->append(tag);
  147. msg->append(pLen,sizeof(dwLength));
  148. msg->append(sMsg);
  149. boost::asio::async_write(m_socket,boost::asio::buffer(*msg,msg->size()),
  150. boost::bind(&socket_session::handle_write,shared_from_this(),
  151. boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred,
  152. msg));
  153. }
  154. catch(std::exception&e)
  155. {
  156. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:["<<e.what()<<"]");
  157. close();
  158. }
  159. catch(...)
  160. {
  161. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<get_remote_addr()<<"],socket异常:[未知异常]");
  162. close();
  163. }
  164. }
  165. voidsocket_session::async_write(message&msg)
  166. {
  167. std::stringdata;
  168. message_oarchive(data,msg);
  169. async_write(data);
  170. }
  171. }

接受数据时,socket_session会先读取7个字节的head,比较前3个字节“KDS”,然后取得4个字节的Length,再读出Length长度的数据,最后将该数据传给read_data_cb回调函数处理,read_data_cb回调函数是在外部注册的。

3.连接管理器

对于服务器来说,它同时服务多个客户端,为了有效的管理,因此需要一个连接管理器,我定义为session_manager。session_manager主要是对socket_session的增删改查,和有效性检查。

//session_manager.h

  1. #pragmaonce
  2. #include"socket_session.h"
  3. #include"filter_container.h"
  4. #include<boost/date_time/posix_time/posix_time.hpp>
  5. #include<boost/multi_index_container.hpp>
  6. #include<boost/multi_index/member.hpp>
  7. #include<boost/multi_index/ordered_index.hpp>
  8. #include<boost/typeof/typeof.hpp>
  9. #include<boost/random.hpp>
  10. #include<boost/pool/detail/singleton.hpp>
  11. namespacefirebird{
  12. template<typenameT>
  13. classvar_gen_wraper
  14. {
  15. public:
  16. var_gen_wraper():gen(boost::mt19937((boost::int32_t)std::time(0)),
  17. boost::uniform_smallint<>(1,100)){}
  18. typenameT::result_typeoperator()(){returngen();}
  19. private:
  20. Tgen;
  21. };
  22. structsession_stu
  23. {
  24. DWORDid;
  25. WORDbusiness_type;
  26. std::stringaddress;
  27. DWORDapp_id;
  28. socket_session_ptrsession;
  29. };
  30. structsid{};
  31. structsbusiness_type{};
  32. structsaddress{};
  33. structsapp_id{};
  34. enumsession_idx_member{session_id=0,session_business_type,session_address,app_id};
  35. #defineCLIENT0
  36. #defineSERVER1
  37. typedefboost::multi_index::multi_index_container<
  38. session_stu,
  39. boost::multi_index::indexed_by<
  40. boost::multi_index::ordered_unique<
  41. boost::multi_index::tag<sid>,BOOST_MULTI_INDEX_MEMBER(session_stu,DWORD,id)>,
  42. boost::multi_index::ordered_non_unique<
  43. boost::multi_index::tag<sbusiness_type>,BOOST_MULTI_INDEX_MEMBER(session_stu,WORD,business_type)>,
  44. boost::multi_index::ordered_non_unique<
  45. boost::multi_index::tag<saddress>,BOOST_MULTI_INDEX_MEMBER(session_stu,std::string,address)>,
  46. boost::multi_index::ordered_non_unique<
  47. boost::multi_index::tag<sapp_id>,BOOST_MULTI_INDEX_MEMBER(session_stu,DWORD,app_id)>
  48. >
  49. >session_set;
  50. #defineMULTI_MEMBER_CON(Tag)boost::multi_index::index<session_set,Tag>::type&
  51. #defineMULTI_MEMBER_ITR(Tag)boost::multi_index::index<session_set,Tag>::type::iterator
  52. structis_business_type{
  53. is_business_type(WORDtype)
  54. :m_type(type)
  55. {
  56. }
  57. booloperator()(constsession_stu&s)
  58. {
  59. return(s.business_type==m_type);
  60. }
  61. WORDm_type;
  62. };
  63. classsession_manager
  64. {
  65. public:
  66. typedefboost::shared_lock<boost::shared_mutex>readLock;
  67. typedefboost::unique_lock<boost::shared_mutex>writeLock;
  68. session_manager(boost::asio::io_service&io_srv,inttype,intexpires_time);
  69. ~session_manager();
  70. voidadd_session(socket_session_ptrp);
  71. voidupdate_session(socket_session_ptrp);
  72. template<typenameTag,typenameMember>
  73. voiddel_session(Memberm)
  74. {
  75. writeLocklock(m_mutex);
  76. if(m_sessions.empty())
  77. {
  78. return;
  79. }
  80. MULTI_MEMBER_CON(Tag)idx=boost::multi_index::get<Tag>(m_sessions);
  81. //BOOST_AUTO(idx,boost::multi_index::get<Tag>(m_sessions));
  82. BOOST_AUTO(iter,idx.find(m));
  83. if(iter!=idx.end())
  84. {
  85. idx.erase(iter);
  86. }
  87. }
  88. //获取容器中的第一个session
  89. template<typenameTag,typenameMember>
  90. socket_session_ptrget_session(Memberm)
  91. {
  92. readLocklock(m_mutex);
  93. if(m_sessions.empty())
  94. {
  95. returnsocket_session_ptr();
  96. }
  97. MULTI_MEMBER_CON(Tag)idx=boost::multi_index::get<Tag>(m_sessions);
  98. BOOST_AUTO(iter,idx.find(m));
  99. returniter!=boost::end(idx)?iter->session:socket_session_ptr();
  100. }
  101. //随机获取容器中的session
  102. template<typenameTag>
  103. socket_session_ptrget_session_by_business_type(WORDm)
  104. {
  105. typedeffilter_container<is_business_type,MULTI_MEMBER_ITR(Tag)>FilterContainer;
  106. readLocklock(m_mutex);
  107. if(m_sessions.empty())
  108. {
  109. returnsocket_session_ptr();
  110. }
  111. MULTI_MEMBER_CON(Tag)idx=boost::multi_index::get<Tag>(m_sessions);
  112. //对容器的元素条件过滤
  113. is_business_typepredicate(m);
  114. FilterContainerfc(predicate,idx.begin(),idx.end());
  115. FilterContainer::FilterIteriter=fc.begin();
  116. if(fc.begin()==fc.end())
  117. {
  118. returnsocket_session_ptr();
  119. }
  120. //typedefboost::variate_generator<boost::mt19937,boost::uniform_smallint<>>var_gen;
  121. //typedefboost::details::pool::singleton_default<var_gen_wraper<var_gen>>s_var_gen;
  122. ////根据随机数产生session
  123. //s_var_gen::object_type&gen=s_var_gen::instance();
  124. //intstep=gen()%fc.szie();
  125. intstep=m_next_session%fc.szie();
  126. ++m_next_session;
  127. for(inti=0;i<step;++i)
  128. {
  129. iter++;
  130. }
  131. returniter!=fc.end()?iter->session:socket_session_ptr();
  132. }
  133. //根据类型和地址取session
  134. template<typenameTag>
  135. socket_session_ptrget_session_by_type_ip(WORDm,std::string&ip)
  136. {
  137. typedeffilter_container<is_business_type,MULTI_MEMBER_ITR(Tag)>FilterContainer;
  138. readLocklock(m_mutex);
  139. if(m_sessions.empty())
  140. {
  141. returnsocket_session_ptr();
  142. }
  143. MULTI_MEMBER_CON(Tag)idx=boost::multi_index::get<Tag>(m_sessions);
  144. //对容器的元素条件过滤
  145. is_business_typepredicate(m);
  146. FilterContainerfc(predicate,idx.begin(),idx.end());
  147. FilterContainer::FilterIteriter=fc.begin();
  148. if(fc.begin()==fc.end())
  149. {
  150. returnsocket_session_ptr();
  151. }
  152. while(iter!=fc.end())
  153. {
  154. if(iter->session->get_remote_addr().find(ip)!=std::string::npos)
  155. {
  156. break;
  157. }
  158. iter++;
  159. }
  160. returniter!=fc.end()?iter->session:socket_session_ptr();
  161. }
  162. //根据类型和app_id取session
  163. template<typenameTag>
  164. socket_session_ptrget_session_by_type_appid(WORDm,DWORDapp_id)
  165. {
  166. typedeffilter_container<is_business_type,MULTI_MEMBER_ITR(Tag)>FilterContainer;
  167. readLocklock(m_mutex);
  168. if(m_sessions.empty())
  169. {
  170. returnsocket_session_ptr();
  171. }
  172. MULTI_MEMBER_CON(Tag)idx=boost::multi_index::get<Tag>(m_sessions);
  173. //对容器的元素条件过滤
  174. is_business_typepredicate(m);
  175. FilterContainerfc(predicate,idx.begin(),idx.end());
  176. FilterContainer::FilterIteriter=fc.begin();
  177. if(fc.begin()==fc.end())
  178. {
  179. returnsocket_session_ptr();
  180. }
  181. while(iter!=fc.end())
  182. {
  183. if(iter->session->get_app_id()==app_id)
  184. {
  185. break;
  186. }
  187. iter++;
  188. }
  189. returniter!=fc.end()?iter->session:socket_session_ptr();
  190. }
  191. private:
  192. intm_type;
  193. intm_expires_time;
  194. boost::asio::io_service&m_io_srv;
  195. boost::asio::deadline_timerm_check_tick;
  196. boost::shared_mutexm_mutex;
  197. unsignedshortm_next_session;
  198. session_setm_sessions;
  199. voidcheck_connection();
  200. };
  201. }

这里主要用到了boost的multi_index容器,这是一个非常有用方便的容器,可实现容器的多列索引,具体的使用方法,在这里不多做详解。

//session_manager.cpp

  1. #include"session_manager.h"
  2. namespacefirebird{
  3. session_manager::session_manager(boost::asio::io_service&io_srv,inttype,intexpires_time)
  4. :m_io_srv(io_srv),m_check_tick(io_srv),m_type(type),m_expires_time(expires_time),m_next_session(0)
  5. {
  6. check_connection();
  7. }
  8. session_manager::~session_manager()
  9. {
  10. }
  11. //检查服务器所有session的连接状态
  12. voidsession_manager::check_connection()
  13. {
  14. try{
  15. writeLocklock(m_mutex);
  16. session_set::iteratoriter=m_sessions.begin();
  17. while(iter!=m_sessions.end())
  18. {
  19. LOG4CXX_DEBUG(firebird_log,"循环");
  20. if(CLIENT==m_type)//客户端的方式
  21. {
  22. if(!iter->session->socket().is_open())//已断开,删除已断开的连接
  23. {
  24. LOG4CXX_INFO(firebird_log,"重新连接["<<iter->address<<"]");
  25. iter->session->close();//通过关闭触发客户端重连
  26. }
  27. else{//连接中,发送心跳
  28. messagemsg;
  29. msg.command=heartbeat;
  30. msg.business_type=iter->session->get_business_type();
  31. msg.app_id=iter->session->get_app_id();
  32. msg.data()="H";
  33. iter->session->async_write(msg);
  34. iter->session->set_op_time();
  35. }
  36. }
  37. elseif(SERVER==m_type)//服务器的方式
  38. {
  39. if(!iter->session->socket().is_open())//已断开,删除已断开的连接
  40. {
  41. LOG4CXX_INFO(firebird_log,KDS_CODE_INFO<<"删除已关闭的session:["<<iter->session->get_remote_addr()<<"]");
  42. iter=m_sessions.erase(iter);
  43. continue;
  44. }
  45. else{//连接中,设定每30秒检查一次
  46. if(iter->session->is_timeout())//如果session已长时间没操作,则关闭
  47. {
  48. LOG4CXX_INFO(firebird_log,KDS_CODE_INFO<<"删除已超时的session:["<<iter->session->get_remote_addr()<<"]");
  49. iter->session->close();//通过关闭触发删除session
  50. }
  51. }
  52. iter->session->set_op_time();
  53. }
  54. else{
  55. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"unknownmanager_type");
  56. }
  57. ++iter;
  58. }
  59. LOG4CXX_DEBUG(firebird_log,"定时检查");
  60. m_check_tick.expires_from_now(boost::posix_time::seconds(m_expires_time));
  61. m_check_tick.async_wait(boost::bind(&session_manager::check_connection,this));
  62. }
  63. catch(std::exception&e)
  64. {
  65. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"["<<e.what()<<"]");
  66. }
  67. catch(...)
  68. {
  69. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"unknownexception.");
  70. }
  71. }
  72. voidsession_manager::add_session(socket_session_ptrp)
  73. {
  74. writeLocklock(m_mutex);
  75. session_stustuSession;
  76. stuSession.id=p->id();
  77. stuSession.business_type=0;
  78. stuSession.address=p->get_remote_addr();
  79. stuSession.app_id=p->get_app_id();
  80. stuSession.session=p;
  81. m_sessions.insert(stuSession);
  82. }
  83. voidsession_manager::update_session(socket_session_ptrp)
  84. {
  85. writeLocklock(m_mutex);
  86. if(m_sessions.empty())
  87. {
  88. return;
  89. }
  90. MULTI_MEMBER_CON(sid)idx=boost::multi_index::get<sid>(m_sessions);
  91. BOOST_AUTO(iter,idx.find(p->id()));
  92. if(iter!=idx.end())
  93. {
  94. const_cast<session_stu&>(*iter).business_type=p->get_business_type();
  95. const_cast<session_stu&>(*iter).app_id=p->get_app_id();
  96. }
  97. }
  98. }

这个时候,我就可以使用id、business_type、address、app_id当做key来索引socket_session了,单使用map容器是做不到的。

还有索引时,需要的一个条件过滤器

//filter_container.h

  1. #pragmaonce
  2. #include<boost/iterator/filter_iterator.hpp>
  3. namespacefirebird{
  4. template<classPredicate,classIterator>
  5. classfilter_container
  6. {
  7. public:
  8. typedefboost::filter_iterator<Predicate,Iterator>FilterIter;
  9. filter_container(Predicatep,Iteratorbegin,Iteratorend)
  10. :m_begin(p,begin,end),
  11. m_end(p,end,end)
  12. {
  13. }
  14. ~filter_container(){}
  15. FilterIterbegin(){returnm_begin;}
  16. FilterIterend(){returnm_end;}
  17. intszie(){
  18. inti=0;
  19. FilterIterfi=m_begin;
  20. while(fi!=m_end)
  21. {
  22. ++i;
  23. ++fi;
  24. }
  25. returni;
  26. }
  27. private:
  28. FilterIterm_begin;
  29. FilterIterm_end;
  30. };
  31. }

4.服务器端的实现

服务器我定义为server_socket_utils,拥有一个session_manager,每当accept成功得到一个socket_session时,都会将其增加到session_manager去管理,注册相关回调函数。

read_data_callback 接收到数据的回调函数

收到数据之后,也就是数据包的body部分,反序列化出command、business_type、app_id和data(我使用到了thrift),如果command==normal正常的业务包,会调用handle_read_data传入data。

close_callback 关闭socket_session触发的回调函数

根据id将该连接从session_manager中删除掉

//server_socket_utils.h

  1. #pragmaonce
  2. #include"socket_session.h"
  3. #include"session_manager.h"
  4. #include<boost/format.hpp>
  5. #include<firebird/message/message.hpp>
  6. namespacefirebird{
  7. usingboost::asio::ip::tcp;
  8. classFIREBIRD_DECLserver_socket_utils
  9. {
  10. private:
  11. boost::asio::io_servicem_io_srv;
  12. boost::asio::io_service::workm_work;
  13. tcp::acceptorm_acceptor;
  14. voidhandle_accept(socket_session_ptrsession,constboost::system::error_code&error);
  15. voidclose_callback(socket_session_ptrsession);
  16. voidread_data_callback(constboost::system::error_code&e,
  17. socket_session_ptrsession,message&msg);
  18. protected:
  19. virtualvoidhandle_read_data(message&msg,socket_session_ptrpSession)=0;
  20. public:
  21. server_socket_utils(intport);
  22. ~server_socket_utils(void);
  23. voidstart();
  24. boost::asio::io_service&get_io_service(){returnm_io_srv;}
  25. session_managerm_manager;
  26. };
  27. }

//server_socket_utils.cpp

  1. #include"server_socket_utils.h"
  2. namespacefirebird{
  3. server_socket_utils::server_socket_utils(intport)
  4. :m_work(m_io_srv),
  5. m_acceptor(m_io_srv,tcp::endpoint(tcp::v4(),port)),
  6. m_manager(m_io_srv,SERVER,3)
  7. {
  8. //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
  9. ////关闭连接前留0秒给客户接收数据
  10. //m_acceptor.set_option(boost::asio::ip::tcp::acceptor::linger(true,0));
  11. //m_acceptor.set_option(boost::asio::ip::tcp::no_delay(true));
  12. //m_acceptor.set_option(boost::asio::socket_base::keep_alive(true));
  13. //m_acceptor.set_option(boost::asio::socket_base::receive_buffer_size(16384));
  14. }
  15. server_socket_utils::~server_socket_utils(void)
  16. {
  17. }
  18. voidserver_socket_utils::start()
  19. {
  20. try{
  21. socket_session_ptrnew_session(newsocket_session(m_io_srv));
  22. m_acceptor.async_accept(new_session->socket(),
  23. boost::bind(&server_socket_utils::handle_accept,this,new_session,
  24. boost::asio::placeholders::error));
  25. }
  26. catch(std::exception&e)
  27. {
  28. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:["<<e.what()<<"]");
  29. }
  30. catch(...)
  31. {
  32. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:[未知异常]");
  33. }
  34. }
  35. voidserver_socket_utils::handle_accept(socket_session_ptrsession,constboost::system::error_code&error)
  36. {
  37. if(!error)
  38. {
  39. try{
  40. socket_session_ptrnew_session(newsocket_session(m_io_srv));
  41. m_acceptor.async_accept(new_session->socket(),
  42. boost::bind(&server_socket_utils::handle_accept,this,new_session,
  43. boost::asio::placeholders::error));
  44. if(session!=NULL)
  45. {
  46. //注册关闭回调函数
  47. session->installCloseCallBack(boost::bind(&server_socket_utils::close_callback,this,_1));
  48. //注册读到数据回调函数
  49. session->installReadDataCallBack(boost::bind(&server_socket_utils::read_data_callback,this,_1,_2,_3));
  50. boost::formatfmt("%1%:%2%");
  51. fmt%session->socket().remote_endpoint().address().to_string();
  52. fmt%session->socket().remote_endpoint().port();
  53. session->set_remote_addr(fmt.str());
  54. session->start();
  55. m_manager.add_session(session);
  56. }
  57. }
  58. catch(std::exception&e)
  59. {
  60. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:["<<e.what()<<"]");
  61. }
  62. catch(...)
  63. {
  64. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:[未知异常]");
  65. }
  66. }
  67. }
  68. voidserver_socket_utils::close_callback(socket_session_ptrsession)
  69. {
  70. LOG4CXX_DEBUG(firebird_log,"close_callback");
  71. try{
  72. m_manager.del_session<sid>(session->id());
  73. }
  74. catch(std::exception&e)
  75. {
  76. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:["<<e.what()<<"]");
  77. }
  78. catch(...)
  79. {
  80. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:[未知异常]");
  81. }
  82. }
  83. voidserver_socket_utils::read_data_callback(constboost::system::error_code&e,
  84. socket_session_ptrsession,message&msg)
  85. {
  86. try{
  87. LOG4CXX_DEBUG(firebird_log,"command=["<<msg.command<<"],["
  88. <<msg.business_type<<"],["<<msg.data()<<"]");
  89. if(msg.command==heartbeat)
  90. {//心跳
  91. session->async_write(msg);
  92. }
  93. elseif(msg.command==regist)
  94. {//注册
  95. session->set_business_type(msg.business_type);
  96. session->set_app_id(msg.app_id);
  97. m_manager.update_session(session);
  98. session->async_write(msg);
  99. LOG4CXX_FATAL(firebird_log,"远程地址:["<<session->get_remote_addr()<<"],服务器类型:["<<
  100. session->get_business_type()<<"],服务器ID:["<<session->get_app_id()<<"]注册成功!");
  101. }
  102. elseif(msg.command==normal)
  103. {//业务数据
  104. handle_read_data(msg,session);
  105. }
  106. else
  107. {
  108. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"收到非法消息包!");
  109. }
  110. }
  111. catch(std::exception&e)
  112. {
  113. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:["<<e.what()<<"]");
  114. }
  115. catch(...)
  116. {
  117. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"socket异常:[未知异常]");
  118. }
  119. }
  120. }

5.客户端

客户端与服务器的逻辑也差不多,区别就是在于客户端通过connect得到socket_session,而服务器是通过accept得到socket_session。

//client_socket_utils.h

  1. #pragmaonce
  2. #include"socket_session.h"
  3. #include"session_manager.h"
  4. #include<boost/algorithm/string.hpp>
  5. #include<firebird/message/message.hpp>
  6. namespacefirebird{
  7. classFIREBIRD_DECLclient_socket_utils
  8. {
  9. public:
  10. client_socket_utils();
  11. ~client_socket_utils();
  12. voidsession_connect(std::vector<socket_session_ptr>&vSession);
  13. voidsession_connect(socket_session_ptrpSession);
  14. //socket_session_ptrget_session(std::string&addr);
  15. boost::asio::io_service&get_io_service(){returnm_io_srv;}
  16. protected:
  17. virtualvoidhandle_read_data(message&msg,socket_session_ptrpSession)=0;
  18. private:
  19. boost::asio::io_servicem_io_srv;
  20. boost::asio::io_service::workm_work;
  21. session_managerm_manager;
  22. voidhandle_connect(constboost::system::error_code&error,
  23. tcp::resolver::iteratorendpoint_iterator,socket_session_ptrpSession);
  24. voidclose_callback(socket_session_ptrsession);
  25. voidread_data_callback(constboost::system::error_code&e,
  26. socket_session_ptrsession,message&msg);
  27. };
  28. }

//client_socket_utils.cpp
  1. #include"client_socket_utils.h"
  2. namespacefirebird{
  3. client_socket_utils::client_socket_utils()
  4. :m_work(m_io_srv),m_manager(m_io_srv,CLIENT,3)
  5. {
  6. }
  7. client_socket_utils::~client_socket_utils()
  8. {
  9. }
  10. voidclient_socket_utils::session_connect(std::vector<socket_session_ptr>&vSession)
  11. {
  12. for(inti=0;i<vSession.size();++i)
  13. {
  14. session_connect(vSession[i]);
  15. }
  16. }
  17. voidclient_socket_utils::session_connect(socket_session_ptrpSession)
  18. {
  19. std::string&addr=pSession->get_remote_addr();
  20. try{
  21. //注册关闭回调函数
  22. pSession->installCloseCallBack(boost::bind(&client_socket_utils::close_callback,this,_1));
  23. //注册读到数据回调函数
  24. pSession->installReadDataCallBack(boost::bind(&client_socket_utils::read_data_callback,this,_1,_2,_3));
  25. std::vector<std::string>ip_port;
  26. boost::split(ip_port,addr,boost::is_any_of(":"));
  27. if(ip_port.size()<2)
  28. {
  29. //throwstd::runtime_error("ip格式不正确!");
  30. LOG4CXX_ERROR(firebird_log,"["<<addr<<"]ip格式不正确!");
  31. return;
  32. }
  33. tcp::resolverresolver(pSession->socket().get_io_service());
  34. tcp::resolver::queryquery(ip_port[0],ip_port[1]);
  35. tcp::resolver::iteratorendpoint_iterator=resolver.resolve(query);
  36. //pSession->set_begin_endpoint(endpoint_iterator);//设置起始地址,以便重连
  37. //由于客户端是不断重连的,即使还未连接也要保存该session
  38. m_manager.add_session(pSession);
  39. tcp::endpointendpoint=*endpoint_iterator;
  40. pSession->socket().async_connect(endpoint,
  41. boost::bind(&client_socket_utils::handle_connect,this,
  42. boost::asio::placeholders::error,++endpoint_iterator,pSession));
  43. }
  44. catch(std::exception&e)
  45. {
  46. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<addr<<"],socket异常:["<<e.what()<<"]");
  47. }
  48. catch(...)
  49. {
  50. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<addr<<"],socket异常:[未知异常]");
  51. }
  52. }
  53. voidclient_socket_utils::handle_connect(constboost::system::error_code&error,
  54. tcp::resolver::iteratorendpoint_iterator,socket_session_ptrpSession)
  55. {
  56. LOG4CXX_DEBUG(firebird_log,KDS_CODE_INFO<<"enter.");
  57. std::stringsLog;
  58. try{
  59. if(!error)
  60. {
  61. LOG4CXX_FATAL(firebird_log,"服务器:["<<pSession->get_business_type()<<"],连接远程地址:["<<pSession->get_remote_addr().c_str()<<"]成功!");
  62. pSession->start();
  63. //向服务器注册服务类型
  64. messagemsg;
  65. msg.command=regist;
  66. msg.business_type=pSession->get_business_type();
  67. msg.app_id=pSession->get_app_id();
  68. msg.data()="R";
  69. pSession->async_write(msg);
  70. }
  71. elseif(endpoint_iterator!=tcp::resolver::iterator())
  72. {
  73. LOG4CXX_ERROR(firebird_log,"连接远程地址:["<<pSession->get_remote_addr().c_str()<<"]失败,试图重连下一个地址。");
  74. pSession->socket().close();//此处用socket的close,不应用session的close触发连接,不然会导致一直重连
  75. tcp::endpointendpoint=*endpoint_iterator;
  76. pSession->socket().async_connect(endpoint,
  77. boost::bind(&client_socket_utils::handle_connect,this,
  78. boost::asio::placeholders::error,++endpoint_iterator,pSession));
  79. }
  80. else
  81. {
  82. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<pSession->get_remote_addr().c_str()<<"]失败!");
  83. pSession->socket().close();//此处用socket的close,不应用session的close触发连接,不然会导致一直重连
  84. }
  85. }
  86. catch(std::exception&e)
  87. {
  88. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<pSession->get_remote_addr().c_str()<<"],socket异常:["<<e.what()<<"]");
  89. }
  90. catch(...)
  91. {
  92. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<pSession->get_remote_addr().c_str()<<"],socket异常:[未知异常]");
  93. }
  94. }
  95. voidclient_socket_utils::read_data_callback(constboost::system::error_code&e,
  96. socket_session_ptrsession,message&msg)
  97. {
  98. LOG4CXX_DEBUG(firebird_log,"command=["<<msg.command<<"],["
  99. <<msg.business_type<<"],["<<msg.data()<<"]");
  100. if(msg.command==heartbeat)
  101. {//心跳
  102. }
  103. elseif(msg.command==regist)
  104. {//注册
  105. LOG4CXX_FATAL(firebird_log,"服务器:["<<session->get_business_type()<<"]注册成功。");
  106. }
  107. elseif(msg.command==normal)
  108. {//业务数据
  109. handle_read_data(msg,session);
  110. }
  111. else
  112. {
  113. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"收到非法消息包!");
  114. }
  115. }
  116. //关闭session就会重连
  117. voidclient_socket_utils::close_callback(socket_session_ptrsession)
  118. {
  119. LOG4CXX_DEBUG(firebird_log,KDS_CODE_INFO<<"enter.");
  120. try{
  121. //tcp::resolver::iteratorendpoint_iterator=context.session->get_begin_endpoint();
  122. std::string&addr=session->get_remote_addr();
  123. std::vector<std::string>ip_port;
  124. boost::split(ip_port,addr,boost::is_any_of(":"));
  125. if(ip_port.size()<2)
  126. {
  127. LOG4CXX_ERROR(firebird_log,"["<<addr<<"]ip格式不正确!");
  128. return;
  129. }
  130. tcp::resolverresolver(session->socket().get_io_service());
  131. tcp::resolver::queryquery(ip_port[0],ip_port[1]);
  132. tcp::resolver::iteratorendpoint_iterator=resolver.resolve(query);
  133. tcp::endpointendpoint=*endpoint_iterator;
  134. session->socket().async_connect(endpoint,
  135. boost::bind(&client_socket_utils::handle_connect,this,
  136. boost::asio::placeholders::error,++endpoint_iterator,session));
  137. }
  138. catch(std::exception&e)
  139. {
  140. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<session->get_remote_addr().c_str()<<"],socket异常:["<<e.what()<<"]");
  141. }
  142. catch(...)
  143. {
  144. LOG4CXX_ERROR(firebird_log,KDS_CODE_INFO<<"连接远程地址:["<<session->get_remote_addr().c_str()<<"],socket异常:[未知异常]");
  145. }
  146. }
  147. }

5.对象串行化

socket_session发送和接收数据包的时候使用到了对象串行化,我这里是通过thrift实现的,其实boost的serialization库也提供了这样的功能,使用起来更为方便,但我在测试过程中,thrift相比之下性能会高很多,因此就坚持使用thrift了,感兴趣的话可以看我之前写的使用thrift串行化对象轻量级序列化库boost serialization》

5.1字符串与thrift对象的相互转换

  1. #pragmaonce
  2. #include<boost/shared_ptr.hpp>
  3. #include<transport/TBufferTransports.h>
  4. #include<protocol/TProtocol.h>
  5. #include<protocol/TBinaryProtocol.h>
  6. namespacefirebird{
  7. usingnamespaceapache::thrift;
  8. usingnamespaceapache::thrift::transport;
  9. usingnamespaceapache::thrift::protocol;
  10. template<typenameT>
  11. voidthrift_iserialize(T&stu,std::string&s)
  12. {
  13. boost::shared_ptr<TMemoryBuffer>trans(newTMemoryBuffer((uint8_t*)&s[0],s.size()));
  14. boost::shared_ptr<TProtocol>proto(newTBinaryProtocol(trans));
  15. stu.read(proto.get());
  16. }
  17. template<typenameT>
  18. voidthrift_oserialize(T&stu,std::string&s)
  19. {
  20. boost::shared_ptr<TMemoryBuffer>trans(newTMemoryBuffer());
  21. boost::shared_ptr<TProtocol>proto(newTBinaryProtocol(trans));
  22. stu.write(proto.get());
  23. s=trans->getBufferAsString();
  24. }
  25. }

5.2通过thrift对象,普通的对象与字符串的相互转换
  1. #pragmaonce
  2. #include"message_archive.hpp"
  3. #include<firebird/archive/thrift_archive.hpp>
  4. #include<firebird/message/TMessage_types.h>
  5. namespacefirebird
  6. {
  7. /***messagetoThriftMessage***/
  8. voidmsg_to_tmsg(TMessage&tmsg,message&msg)
  9. {
  10. //设置
  11. tmsg.command=msg.command;
  12. tmsg.business_type=msg.business_type;
  13. tmsg.app_id=msg.app_id;
  14. //设置context
  15. tmsg.context.cmdVersion=msg.context().cmdVersion;
  16. tmsg.context.cpid.swap(msg.context().cpid);
  17. tmsg.context.remote_ip.swap(msg.context().remote_ip);
  18. tmsg.context.wSerialNumber=msg.context().wSerialNumber;
  19. tmsg.context.session_id=msg.context().session_id;
  20. //设置source
  21. for(inti=0;i<msg.source().size();++i)
  22. {
  23. tmsg.source.push_back(msg.source()[i]);
  24. }
  25. //设置destination
  26. for(inti=0;i<msg.destination().size();++i)
  27. {
  28. tmsg.destination.push_back(msg.destination()[i]);
  29. }
  30. //设置data
  31. tmsg.data=msg.data();
  32. }
  33. /***ThriftMessagetomessage***/
  34. voidtmsg_to_msg(message&msg,TMessage&tmsg)
  35. {
  36. //设置
  37. msg.command=tmsg.command;
  38. msg.business_type=tmsg.business_type;
  39. msg.app_id=tmsg.app_id;
  40. //设置context
  41. msg.context().cmdVersion=tmsg.context.cmdVersion;
  42. msg.context().cpid=tmsg.context.cpid;
  43. msg.context().remote_ip=tmsg.context.remote_ip;
  44. msg.context().wSerialNumber=tmsg.context.wSerialNumber;
  45. msg.context().session_id=tmsg.context.session_id;
  46. //设置source
  47. for(inti=0;i<tmsg.source.size();++i)
  48. {
  49. msg.source()<<tmsg.source[i];
  50. }
  51. //设置destination
  52. for(inti=0;i<tmsg.destination.size();++i)
  53. {
  54. msg.destination()<<tmsg.destination[i];
  55. }
  56. //设置data
  57. msg.data()=tmsg.data;
  58. }
  59. voidmessage_iarchive(message&msg,std::string&s)
  60. {
  61. TMessagetmsg;
  62. thrift_iserialize(tmsg,s);
  63. tmsg_to_msg(msg,tmsg);
  64. }
  65. voidmessage_oarchive(std::string&s,message&msg)
  66. {
  67. TMessagetmsg;
  68. msg_to_tmsg(tmsg,msg);
  69. thrift_oserialize(tmsg,s);
  70. }
  71. }

---恢复内容结束---

免责声明:文章转载自《boost asio 异步实现tcp通讯》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用 supervisor 管理 Celery 服务Python 启动 FastAPI 报错 Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

聊聊MySQL、HBase、ES的特点和区别(转载http://www.360doc.com/content/17/0703/08/44827304_668358202.shtml)

互联网时代各种存储框架层出不穷,眼花缭乱,比如传统的关系型数据库:Oracle、MySQL;新兴的NoSQL:HBase、Cassandra、Redis;全文检索框架:ES、Solr等。如何为自己的业务选取合适的存储方案,相信大家都思考过这个问题,本文简单聊聊我对MySQL、HBase、ES的理解,希望能和大家一起探讨进步,有不对的地方还请指出。 MySQ...

Android中实现TCP和UDP传输实例

TCP和UDP在网络传输中非常重要,在Android开发中同样重要。 首先我们来看一下什么是TCP和UDP。 什么是TCP? TCP:Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specifi...

keepalived日志

默认日志存放在/var/log/messages 一、查看配置文件 # Options for keepalived. See `keepalived --help'output and keepalived(8) and # keepalived.conf(5) man pages fora list of all options. Here are t...

golang学习之rpc实例

rpc(远程过程调用),可以像调用本地程序一样调用远端服务,rpc分为http方式和tcp连接方式,使用http的rpc调用如下: 首先是server端: // rpc_server project main.go package main import ( "fmt" "net" "net/http" "n...

SqlAlchemy

SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。 Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如: 1 MySQL-Python 2...

03 flask数据库操作、flask-session、蓝图

ORM ORM 全拼Object-Relation Mapping,中文意为 对象-关系映射。主要实现模型对象到关系数据库数据的映射。 1.优点 : 只需要面向对象编程, 不需要面向数据库编写代码. 对数据库的操作都转化成对类属性和方法的操作. 不用编写各种数据库的sql语句. 实现了数据模型与数据库的解耦, 屏蔽了不同数据库操作上的差异....