boost asio tcp 多线程异步读写,服务器与客户端。
// server.cpp #if 0
多个线程对同一个io_service 对象处理
用到第三方库:log4cplus, google::protobuf
用到C++11的特性,Windows 需要用到vs2013 gcc 4.8
#endif #include <iostream>
#include <thread>
#include <vector> #include <boost/asio.hpp> #include <boost/shared_array.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp> #include <common.pb.h> void async_accept();
void handle_accept(boost::shared_ptr<boost::asio::ip::tcp::socket> new_conn,
const boost::system::error_code &ec);
void async_read_head(boost::shared_ptr<boost::asio::ip::tcp::socket> conn);
void handle_head(
boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
boost::shared_array<char> sa_len,
const boost::system::error_code &ec,
std::size_t bytes_transfered);
void async_read_proto(boost::shared_ptr<boost::asio::ip::tcp::socket> conn, int32_t len);
void handle_proto(
boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
boost::shared_array<char> sa_data,
const boost::system::error_code &ec,
std::size_t bytes_transfered); boost::asio::io_service io_svc;
boost::asio::ip::address_v4 lis_ip; // 默认监听本机所有IP
boost::asio::ip::tcp::endpoint lis_ep(lis_ip, );
boost::asio::ip::tcp::acceptor acceptor(io_svc, lis_ep); #include <Log.h> // log4cplus 的相关头文件,这里不再一个一个敲出来了。 log4cplus::Logger *gLog = nullptr; static const int PACKAGE_LENGTH = ; int main(int argc, char *argv[])
{
log4cplus::initialize(); static log4cplus::Logger s_log = log4cplus::Logger::getInstance("server");
gLog = &s_log; LOG4CPLUS_INFO_FMT(*gLog, "main begin..."); for (int i = ; i < ; ++i)
{
async_accept();
} // 捕获信号
boost::asio::signal_set signals_(io_svc);
signals_.add(SIGINT);
signals_.add(SIGTERM);
signals_.async_wait([](const boost::system::error_code &ec, int sig)
{
LOG4CPLUS_INFO_FMT(*gLog, "signal: %d, error_message: %s",
sig, ec.message().c_str());
io_svc.stop();
}); std::vector<std::thread> vecThread;
for (int i = ; i < ; ++i)
{
vecThread.emplace_back(std::thread([](){
LOG4CPLUS_INFO_FMT(*gLog, "thread start...");
io_svc.run();
LOG4CPLUS_INFO_FMT(*gLog, "thread finished.");
}));
} for (size_t i = ; i < vecThread.size(); ++i)
{
vecThread[i].join();
}
assert(io_svc.stopped(); #ifdef WIN32
system("pause");
#endif return ;
} // 标记异步监听,投放到指定io_service 对象中
void async_accept()
{
LOG4CPLUS_INFO_FMT(*gLog, "async_accept waitting..."); boost::shared_ptr<boost::asio::ip::tcp::socket> new_sock
= boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(ios_svc)); boost::function<void(const boost::system::error_code &> cb_accept;
cb_accept = boost::bind(handle_accept, new_sock, _1);
acceptor.async_accept(*new_sock, cb_accept);
} // 监听返回的处理
void handle_accept(boost::shared_ptr<boost::asio::ip::tcp::socket> new_conn,
const boost::system::error_code &ec)
{
if (ec != )
{
LOG4CPLUS_INFO(*gLog, "accept failed: " << ec.message()); return;
}
LOG4CPLUS_INFO(*gLog, "a new client connected. " << new_conn->remote_endpoint()); async_read_head(new_conn); // 处理下一个连接,每次处理完了之后,需要再次accept.
// 否则io_service 将只处理一次,然后结束监听。
// 所以这里可以处理一个情况,就是当你要结束监听的时候,人要在这里return
// 那么io_service 的run() 函数就会stop. 但如果还有其他的异步操作被记录,
// run() 函数还是会继续运行,以处理其他的异步操作。
async_accept();
} // 对一个指定的连接标记异步读头部,然后投放到io_service 对象
void async_read_head(boost::shared_ptr<boost::asio::ip::tcp::socket> conn)
{
// 固定报文头长度为${PACKAGE_LENGTH} 个字节
boost::shared_array<char> sa_len(new char[PACKAGE_LENGTH]); // 回调函数
boost::function<void(const boost::system::error_code &, std::size_t)> cb_msg_len;
cb_msg_len = boost::bind(handle_head, conn, sa_len, _1, _2); // 异步读,读一个报文的长度,boost::asio::async_read() 函数有个特点,
// 它会将这里指定的buffer 缓冲区读满了才会回调handle_head 函数。
boost::asio::async_read(
*conn, boost::asio::buffer(sa_len.get(), PACKAGE_LENGTH), cb_msg_len);
} // 头部数据完整读取后的处理函数
void handle_head(
boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
boost::shared_array<char> sa_len,
const boost::system::error_code &ec,
std::size_t bytes_transfered)
{
if (!conn->is_open())
{
LOG4CPLUS_INFO(*gLog, "socket was not opened.");
return ;
} if (ec != )
{
if (ec == boost::asio::error::eof)
{
LOG4CPLUS_INFO(*gLog, "Disconnect from " << conn->remote_endpoint());
}
else
{
LOG4CPLUS_INFO(*gLog, "Error on receive: " << ec.message());
} return ;
} // 这里对的数据做处理
assert(bytes_transfered == PACKAGE_LENGTH);
int32_t len_net = ; // 网络字节序:数据部分长度
int32_t len_loc = ; // 本地字节序:数据部分长度
memcpy(&len_net, sa_len.get(), sizeof(len_net));
len_loc = boost::asio::detail::socket_ops::network_to_host_long(len_net);
LOG4CPLUS_INFO_FMT(*gLog, "nLenLoc: %d", len_loc); async_read_proto(conn, len_loc);
} // 对一个指定的连接标记异步读数据部,然后投放到io_service 对象
void async_read_proto(boost::shared_ptr<boost::asio::ip::tcp::socket> conn, int32_t len)
{
// 数据部分
boost::shared_array<char> sa_data(new char[len]()); // 回调函数
boost::function<void(const boost::system::error_code &, std::size_t)> cb_proto;
cb_proto = boost::bind(handle_proto, conn, sa_data, _1, _2); boost::asio::async_read(*conn,
boost::asio::buffer(sa_data.get(), len), cb_proto);
} // 数据部分读完整后的处理函数
void handle_proto(
boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
boost::shared_array<char> sa_data,
const boost::system::error_code &ec,
std::size_t bytes_transfered)
{
if (!conn->is_open())
{
LOG4CPLUS_INFO(*gLog, "socket was not opened.");
return ;
} if (ec != )
{
if (ec == boost::asio::error::eof)
{
LOG4CPLUS_INFO(*gLog, "Disconnect from " << conn->remote_endpoint());
}
else
{
LOG4CPLUS_INFO(*gLog, "Error on receive: " << ec.message());
}
return ;
} // 处理这个proto 数据
// 这里将这个数组转换成一个proto, 然后处理这个proto
MessageHead pro;
if (!pro.ParseFromArray(sa_data.get(), (int32_t)bytes_transfered))
{
LOG4CPLUS_ERROR_FMT(*gLog, "ParseFromArray() failed");
return ;
} int port = conn->remote_endpoint().port();
LOG4CPLUS_INFO_FMT(*gLog, "port: %d\n%s", port, pro.DebugString().c_str(); // 处理完了之后,类似accept 的异步调用一样,需要继续调用异步的读数据
// 同样的,如果要结束一个连接,正常的结算应该在这里return 调用。
// 当然了,使用socket 的close(), shut_down() 函数也可以关闭这个连接。
async_read_head(conn);
}
// client.cpp #include <iostream>
#include <thread>
#include <vector> #include <boost/asio.hpp> #include <boost/shared_array.hpp>
#include <boost/make_shared.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp> #include <boost/pool/pool.hpp>
#include <boost/pool/singleton_pool.hpp> // proto buffer 生成的头文件
#include <common.pb.h> void async_connect();
void handle_connect(boost::shared_ptr<boost::asio::ip::tcp::socket> new_conn,
const boost::system::error_code &ec);
void async_write(boost::shared_ptr<boost::asio::ip::tcp::socket> conn);
void handle_write_head(boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
const std::shared_ptr<std::string> sp_data_proto,
const boost::system::error_code &ec,
std::size_t bytes_transfered);
void handle_write_proto(boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
const std::shared_ptr<std::string> sp_data_proto,
const boost::system::error_code &ec, std::size_t bytes_transfered); boost::asio::io_service io_svc;
boost::asio::ip::tcp::endpoint svr_ep(
boost::asio::ip::address_v4::from_string("127.0.0.1"), ); #include <Log.h> // log4cplus 的相关头文件,这里不再一个一个敲出来了。 log4cplus::Logger *gLog = nullptr; // 应该是直接使用对象,但是懒得改就保留了。 // 包头固定长度
static const int PACKAGE_LENGTH = ; using pool_head = boost::singleton_pool<struct struHead, PACKAGE_LENGTH>;
using pool_string = boost::singleton_pool<struct struString, sizeof(std::string)>; std::shared_ptr<std::string> createSharedString()
{
std::shared_ptr<std::string> spTp(new (pool_string::malloc()) std::string,
[](std::string *tp)
{
tp->~basic_string();
pool_string::free(tp);
}); return spTp;
} int main(int argc, char *argv[])
{
log4cplus::initialize(); static log4cplus::Logger s_log = log4cplus::Logger::getInstance("client");
gLog = &s_log;
assert(gLog != nullptr); LOG4CPLUS_INFO_FMT(*gLog, "main begin..."); for (int i = ; i < ; ++i)
{
async_connect();
} std::vector<std::thread> vecThread;
for (int i = ; i < ; ++i)
{
vecThread.emplace_back(std::thread([]() {
LOG4CPLUS_INFO_FMT(*gLog, "thread start...");
io_svc.run();
LOG4CPLUS_INFO_FMT(*gLog, "thread finished.");
}));
} for (size_t i = ; i < vecThread.size(); ++i)
{
vecThread[i].join();
}
assert(io_svc.stopped()); #ifdef WIN32
system("pause");
#endif return ;
} void async_connect()
{
LOG4CPLUS_INFO_FMT(*gLog, "async_connect waitting..."); boost::shared_ptr<boost::asio::ip::tcp::socket> new_sock
= boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(io_svc)); new_sock->async_connect(svr_ep, boost::bind(
handle_connect, new_sock,
boost::asio::placeholders::error));
} void handle_connect(boost::shared_ptr<boost::asio::ip::tcp::socket> new_conn,
const boost::system::error_code &ec)
{
if (ec != )
{
LOG4CPLUS_INFO(*gLog, "connect failed: " << ec.message());
return ;
} LOG4CPLUS_INFO(*gLog, "connect success, server: " << new_conn->remote_endpoint()); async_write(new_conn);
} #if 0
message messageHead
{
optional uint32 FunCode = ;
optional uint32 RequestID = ;
optional uint32 AccountId = ;
optional uint32 AccessId = ;
optional int64 ClientTime = ;
optional uint32 GoodsId = ;
optional bytes UUID = ;
}
#endif void async_write(boost::shared_ptr<boost::asio::ip::tcp::socket> conn)
{
MessageHead pro;
pro.set_funcode();
pro.set_requestid();
pro.set_accountid();
pro.set_clienttime(time(NULL));
pro.set_goodsid();
pro.set_uuid(std::string("uuid_500384")); std::shared_ptr<std::string> sp_data = createSharedString();
if (!pro.SerializeToString(sp_data.get())
{
LOG4CPLUS_ERROR_FMT(*gLOg, "SerializeToString failed."); return ;
} LOG4CPLUS_INFO_FMT(*gLog, "data.size() = %lld", sp_data->size()); char ch_head[PACKAGE_LENGTH] = {};
int32_t len_net = boost::asio::detail::socket_ops::host_to_network_long((int32_t)sp_data->size());
memcpy(ch_head, &len_net, sizeof(len_net)); if (sp_data->size() == )
{
return ;
} boost::function<void(const boost::system::error_code&, std::size_t)> cb_write_head;
cb_write_head = boost::bind(handle_write_head, conn, sp_data, _1, _2);
boost::asio::async_write(*conn, boost::asio::buffer(ch_head, PACKAGE_LENGTH), cb_write_head);
} void handle_write_head(boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
const std::shared_ptr<std::string> sp_data_proto,
const boost::system::error_code &ec,
std::size_t bytes_transfered)
{
if (!conn->is_open())
{
LOG4CPLUS_INFO(*gLog, "socket was not opened.");
return;
} if (ec != )
{
if (ec == boost::asio::error::eof)
{
LOG4CPLUS_INFO(*gLog, "Disconnect from " << conn->remote_endpoint());
}
else
{
LOG4CPLUS_INFO(*gLog, "Error on receive: " << ec.message());
} return ;
} boost::function<void(const boost::system::error_code&, std::size_t)> cb_write_proto;
cb_write_proto = boost::bind(handle_write_proto, conn, sp_data_proto, _1, _2);
boost::asio::async_write(*conn, boost::asio::buffer(*sp_data_proto), cb_write_proto);
} // 这里的sp_data_proto 在该函数中并不需要使用,用它作参数的唯一作用,就是保留它的生命周期,
// 保证在数据写完之前它不会被析构。
// 因为,如果该对象在async_write 还未写完之前就被析构的话, 就会造成数据的错乱,最终导致对端的数据是错误的。
void handle_write_proto(boost::shared_ptr<boost::asio::ip::tcp::socket> conn,
const std::shared_ptr<std::string> sp_data_proto,
const boost::system::error_code &ec, std::size_t bytes_transfered)
{
if (!conn->is_open())
{
LOG4CPLUS_INFO(*gLog, "socket was not opened.");
return ;
} if (ec != )
{
if (ec == boost::asio::error::eof)
{
LOG4CPLUS_INFO(*gLog, "Disconnect from " << conn->remote_endpoint());
}
else
{
LOG4CPLUS_INFO(*gLog, "Error on receive: " << ec.message());
} return ;
} LOG4CPLUS_INFO(*gLog, "write proto finished.");
// 数据写完了之后,可以读对端发送过来的数据。
// 如果不再读对端的数据,直接该socket 将会被断开。
//async_read_head(conn);
}
boost asio tcp 多线程异步读写,服务器与客户端。的更多相关文章
- boost asio tcp 多线程
common/pools.h // common/pools.h #pragma once #include <string> #include <boost/pool/pool.h ...
- 可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui)
可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui) 0 前言 >>[前言].[第1节].[第2节].[第3节]. ...
- 异步Socket服务器与客户端
本文灵感来自Andre Azevedo 在CodeProject上面的一片文章,An Asynchronous Socket Server and Client,讲的是异步的Socket通信. S ...
- Linux网络编程--多线程实现echo服务器与客户端“一对多”功能,是网络编程的“Hello World!”
在linux平台下,用多线程实现echo服务器与客户端“一对多”(即是一台服务器可以响应多个客户端的请求).本人写了个demo,和大家一起分享,有不足的地方,请多多指教,我是壮壮熊. 编译时,在后面加 ...
- 浅谈 Boost.Asio 的多线程模型
Boost.Asio 有两种支持多线程的方式,第一种方式比较简单:在多线程的场景下,每个线程都持有一个io_service,并且每个线程都调用各自的io_service的run()方法. 另一种支持多 ...
- <转>浅谈 Boost.Asio 的多线程模型
本文转自:http://senlinzhan.github.io/2017/09/17/boost-asio/ Boost.Asio 有两种支持多线程的方式,第一种方式比较简单:在多线程的场景下,每个 ...
- boost asio tcp server 拆分
从官方给出的示例中对于 boost::asio::ip::tcp::acceptor 类的使用,是直接使用构造函数进行构造对象,这一种方法用来学习是一个不错的方式. 但是要用它来做项目却是不能够满足我 ...
- 使用 boost.asio 简单实现 异步Socket 通信
客户端: class IPCClient { public: IPCClient(); ~IPCClient(); bool run(); private: bool connect(); bool ...
- boost::asio::tcp
同步TCP通信服务端 #include <boost/asio.hpp> #include <iostream> using namespace boost::asio; in ...
随机推荐
- vue PC端页面引入vue-quill-editor富文本插件
项目需要:在添加新类别的弹框中,要在输入框中输入多条描述信息,不同的描述信息要换行输入,输入后点击确定传给后端,接口返回成功后点击查看刚添加的新类别时,描述框中展现多条换行的描述信息也要跟填写时一样( ...
- NOPI Excel 数据导入到数据库
/// <summary> /// 上传excel文件 并将文件数据导入到数据库 /// </summary> /// <param name="file&qu ...
- MS14-068利用
漏洞原理详情后续补上:kerberos 协议实现过程中的某些 bug,致使普通域用户可以任意伪造高权限 PAC,去请求 TGS 从而导致的权限提升,漏洞现在很少遇到了. 一.利用 需要拥有一个域账号的 ...
- transformer模型计算图
参考了这篇文章:http://nlp.seas.harvard.edu/2018/04/03/attention.html 结合代码和图,能更加清楚的了解transformer中的一些原理(ps,等下 ...
- 对malloc与free函数的浅识
本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: .函数原型及说明: void *mal ...
- 39th 迷迷糊糊 二豆玩不转了
今天学的语法 1. # {} . format()的传送作用 请从键盘获取一个整数,求他的平方根,要求: 1 如果这个整数是大于等于0,则直接打印其平方根 2 否则, 打印其绝对值的平方根 x ...
- vue css动画原理
从隐藏到显现 从显现到隐藏 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- spring启动异步线程
大纲: spring启动异步线程 spring配置线程池 一.spring启动异步线程 spring启动异步线程方法就是在方法上加上注解@Async,然后启动类或配置类上加上注解@EnableAsyn ...
- 【JZOJ6435】【luoguP5666】【CSP-S2019】树的重心
description analysis 需要知道一棵树的重心一定在从根出发的重链上,可以考虑先进行树链剖分弄出重儿子和次重儿子,再倍增维护重儿子 由于重链上有一个或两个重心,接下来求的重心都是深度较 ...
- 如何基于 Nacos 和 Sentinel ,实现灰度路由和流量防护一体化
基于Alibaba Nacos和Sentinel,实现灰度路由和流量防护一体化的解决方案,发布在最新的 Nepxion Discovery 5.4.0 版,具体参考: 源码主页,请访问 源码主页指南主 ...