(四)基于BOOST ASIO实现的异步TCP客户端与服务端
代码
//asio_socket.h
#pragma once
#include
#include
#include
#include
#include
#include
//asio_async_tcp_client.hpp
#pragma once
#include "asio_socket.h"
using std::string;
using std::vector;
class CAsyncClient
{
private:
io_service_type m_ios;
work_type m_work;
string m_id;
std::atomic m_stopped;
tcp_endpoint_type m_ep;
tcp_socket_type m_sock;
std::mutex mut_readbuf;
std::mutex mut_write_sock;
std::queue m_readbuf;
std::condition_variable cv_readbuf_notempty;
buffer_type m_readbuf_raw;//TCP原始字节流BUF,待拆包
public:
CAsyncClient(const string & ip, int port, int id)
: m_id("C" + std::to_string(id) + "> ")
, m_stopped(false)
, m_ep(address_v4_type::from_string(ip), port)
, m_sock(m_ios)
, m_work(m_ios)
{
connect_socket();
boost::thread th_read([this]()->void{ m_ios.run(); });
LOG_INFO_GLOG << m_id << "client connected "< lk(mut_readbuf);
cv_readbuf_notempty.wait(lk, [this](){return (!m_readbuf.empty() || m_stopped); });
if (m_stopped)
{
LOG_WARN_GLOG << m_id << "exit pop_readbuf as socket stopped";
return false;
}
msg = m_readbuf.front();
m_readbuf.pop();
return true;
}
private:
void close_socket()
{
error_code_type err_asio;
m_sock.close(err_asio);
m_stopped = true;
cv_readbuf_notempty.notify_all();
LOG_INFO_GLOG << m_id << "closed";
}
void connect_socket()
{
error_code_type err_asio;
do
{
m_sock.connect(m_ep, err_asio);//阻塞
BOOST_SLEEP_MS(20);
} while (err_asio && err_asio != boost::asio::error::already_connected);
m_stopped = false;
m_readbuf_raw.assign(tcp_max_msg_length, 0);
start_async_read();
}
void start_async_read(int last_ready_size=0)
{
//m_readbuf_raw中已存有一部分不完整的报文内容,大小raw_ready_size
if (m_stopped)
{
return;
}
m_sock.async_read_some(boost::asio::buffer(&m_readbuf_raw[last_ready_size],
tcp_max_msg_length - last_ready_size),
boost::bind(&CAsyncClient::handle_async_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
last_ready_size));
}
void handle_async_read(const error_code_type & err, const size_t size, int last_ready_size)
{
if (m_stopped)
{
return;
}
if (err)
{
LOG_WARN_GLOG << m_id << "read failed:" << err.message();
close_socket();
connect_socket();
return;
}
/以下对报文进行拆包///
int ready_size = last_ready_size + size;//未拆包的字节数
int split_size = 0;//已拆包的字节数
while (ready_size>0)
{
if (ready_size<4)
{
std::copy(m_readbuf_raw.begin() + split_size, m_readbuf_raw.end(), m_readbuf_raw.begin());
std::fill(m_readbuf_raw.begin() + (m_readbuf_raw.size() - split_size), m_readbuf_raw.end(), 0);
break;//不足一个头部的大小
}
int cur_msg_length = ntohl(*(int *)(&m_readbuf_raw[split_size]));
if (cur_msg_length <= 0 || cur_msg_length > tcp_max_msg_length-4)//最大接收报文长度限制
{
LOG_WARN_GLOG << m_id << "msg length error,length=" << cur_msg_length;
close_socket();
connect_socket();
return;
}
if (ready_size<4 + cur_msg_length)
{
std::copy(m_readbuf_raw.begin() + split_size, m_readbuf_raw.end(), m_readbuf_raw.begin());
std::fill(m_readbuf_raw.begin() + (m_readbuf_raw.size() - split_size), m_readbuf_raw.end(), 0);
break;//不足一个完整报文大小
}
split_size += 4;//拆头
ready_size -= 4;
string cur_msg((char *)(&m_readbuf_raw[split_size]), cur_msg_length);
LOG_INFO_GLOG << m_id << "recv:" << cur_msg.substr(0, 2048);
push_readbuf(std::move(cur_msg));
split_size += cur_msg_length;//拆MSG
ready_size -= cur_msg_length;
}//while
start_async_read(ready_size);
}
void start_async_write(const string & msg, bool b_log)
{
if (m_stopped)
{
return;
}
int length = (int)msg.size();
length = htonl(length);
string tmp_msg((char *)&length, 4);
tmp_msg.append(msg);
LOG_IF_INFO_GLOG(b_log) << m_id << "send: " << msg;
std::unique_lock lk(mut_write_sock);
boost::asio::async_write(m_sock, boost::asio::buffer(tmp_msg.data(), tmp_msg.size()),
boost::bind(&CAsyncClient::handle_async_write, this, boost::placeholders::_1));
}
void handle_async_write(const error_code_type & err)
{
if (m_stopped)
{
return;
}
if (err)
{
if (err == boost::asio::error::operation_aborted)
{
if (m_sock.is_open())
{
LOG_WARN_GLOG << m_id << "send failed:" << err.message();
}
else
{
LOG_WARN_GLOG << m_id << "send failed:" << err.message();
connect_socket();
}
}
else
{
LOG_WARN_GLOG << m_id << "send failed:" << err.message();
close_socket();
connect_socket();
}
}
}
void push_readbuf(string && msg)
{
{
std::unique_lock lk(mut_readbuf);
if (m_readbuf.size() >= tcp_max_readbuf_size)
{
m_readbuf.pop();
LOG_EVERY_WARN_GLOG(20) << m_id << "readbuf overflow";
}
m_readbuf.emplace(std::move(msg));
}
cv_readbuf_notempty.notify_one();
}
};
typedef boost::shared_ptr pAsyncClientType;
//asio_async_tcp_server.hpp
#pragma once
#include "asio_socket.h"
#include "asio_timer.hpp"
using std::string;
using std::vector;
class CAsyncServer
{
friend class CAsyncAcceptor;
typedef std::pair TMSG;//消息和超时时间
private:
string m_id;
std::atomic m_stopped;
tcp_endpoint_type m_ep;
tcp_socket_type m_sock;
std::mutex mut_readbuf;
std::mutex mut_write_sock;
std::queue m_readbuf;
std::condition_variable cv_readbuf_notempty;
buffer_type m_readbuf_raw;//TCP原始字节流BUF,待拆包
public:
CAsyncServer(io_service_type & ios, int id)
: m_id("S" + std::to_string(id) + "> ")
, m_stopped(false)
, m_ep(address_v4_type::from_string("0.0.0.0"), 0)
, m_sock(ios)
{
}
~CAsyncServer()
{
cv_readbuf_notempty.notify_all();
close_socket();
LOG_INFO_GLOG << m_id << "stop server";
}
inline tcp_socket_type & socket(){ return m_sock; }
inline const string local_ip(){
return m_sock.local_endpoint().address().to_string(); }
inline const string remote_ip(){
return m_sock.remote_endpoint().address().to_string(); }
inline const bool is_stopped(){ return !!m_stopped; }
void push_writebuf(const string & msg, bool b_log = true)
{
start_async_write(msg, b_log);
}
bool pop_readbuf(string & msg)
{
msg.clear();
std::unique_lock lk(mut_readbuf);
cv_readbuf_notempty.wait_for(lk, std::chrono::seconds(tcp_server_read_timeout),
[this](){return (!m_readbuf.empty() || m_stopped); });
if (m_stopped)
{
LOG_WARN_GLOG << m_id << "exit pop_readbuf as socket stopped";
return false;
}
if (m_readbuf.empty())
{
close_socket();
LOG_WARN_GLOG << m_id << "read timeout,close_socket";//心跳超时
return false;
}
msg = m_readbuf.front();
m_readbuf.pop();
return true;
}
private:
void close_socket()
{
try
{
if (!m_stopped)
{
error_code_type err_asio;
m_sock.close(err_asio);
m_stopped = true;
cv_readbuf_notempty.notify_all();
LOG_INFO_GLOG << m_id << "closed";
}
}
catch (std::exception &err)
{
LOG_INFO_GLOG << m_id << "close_socket crash:" << err.what();
}
catch (...)
{
LOG_INFO_GLOG << m_id << "close_socket crash:unknown error";
}
}
void start_async_read(int last_ready_size = 0)
{
if (m_stopped)
{
return;
}
if (last_ready_size<=0)
{
m_readbuf_raw.assign(tcp_max_msg_length, 0);
}
m_sock.async_read_some(boost::asio::buffer(&m_readbuf_raw[last_ready_size],
tcp_max_msg_length - last_ready_size),
boost::bind(&CAsyncServer::handle_async_read,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
last_ready_size));
}
void handle_async_read(const error_code_type & err_, const size_t size_, int last_ready_size)
{
if (m_stopped)
{
return;
}
if (err_)
{
LOG_WARN_GLOG << m_id << "read failed:" << err.message();
close_socket();
return;
}
/以下对报文进行拆包///
int ready_size = last_ready_size + size;//未拆包的字节数
int split_size = 0;//已拆包的字节数
while (ready_size>0)
{
if (ready_size<4)
{
std::copy(m_readbuf_raw.begin() + split_size, m_readbuf_raw.end(), m_readbuf_raw.begin());
std::fill(m_readbuf_raw.begin() + (m_readbuf_raw.size() - split_size), m_readbuf_raw.end(), 0);
break;//不足一个头部的大小
}
int cur_msg_length = ntohl(*(int *)(&m_readbuf_raw[split_size]));
if (cur_msg_length <= 0 || cur_msg_length > tcp_max_msg_length - 4)//最大接收报文长度限制
{
LOG_WARN_GLOG << m_id << "msg length error,length=" << cur_msg_length;
close_socket();
return;
}
if (ready_size < 4 + cur_msg_length)
{
std::copy(m_readbuf_raw.begin() + split_size, m_readbuf_raw.end(), m_readbuf_raw.begin());
std::fill(m_readbuf_raw.begin() + (m_readbuf_raw.size() - split_size), m_readbuf_raw.end(), 0);
break;//不足一个完整报文大小
}
split_size += 4;//拆头
ready_size -= 4;
string cur_msg((char *)(&m_readbuf_raw[split_size]), cur_msg_length);
LOG_INFO_GLOG << m_id << "recv:" << cur_msg.substr(0, 2048);
push_readbuf(std::move(cur_msg));
split_size += cur_msg_length;//拆MSG
ready_size -= cur_msg_length;
}
start_async_read(ready_size);
}
void start_async_write(const string & msg, bool b_log)
{
if (m_stopped)
{
return;
}
int length = (int)msg.size();
length = htonl(length);
string tmp_msg((char *)&length, 4);
tmp_msg.append(msg);
LOG_IF_INFO_GLOG(b_log) << m_id << "send: " << msg;
std::unique_lock lk(mut_write_sock);
boost::asio::async_write(m_sock, boost::asio::buffer(tmp_msg.data(), tmp_msg.size()),
boost::bind(&CAsyncServer::handle_async_write, this, boost::placeholders::_1));
}
void handle_async_write(const error_code_type & err)
{
if (m_stopped)
{
return;
}
if (err)
{
close_socket();
LOG_WARN_GLOG << m_id << "send failed:" << err.message();
}
}
void push_readbuf(string && msg)
{
{
std::unique_lock lk(mut_readbuf);
if (m_readbuf.size() >= tcp_max_readbuf_size)
{
m_readbuf.pop();
LOG_EVERY_WARN_GLOG(20) << m_id << "readbuf overflow";
}
m_readbuf.emplace(std::move(msg));
}
cv_readbuf_notempty.notify_one();
}
};
typedef boost::shared_ptr pAsyncServerType;
class CAsyncAcceptor
{
private:
io_service_type m_ios; //acceptor的监听IOS
tcp_acceptor_type m_acceptor;
std::list m_servers;
std::function m_handle_func;
work_ptr_type m_pwork;
boost::thread_group m_thgroup_srv;
std::vector> m_vec_ios;//供服务端连接套接字使用
std::atomic m_stop_listen;
TimerAsio timer_clear_server_list;
public:
CAsyncAcceptor(int port, int ios_num)
:m_acceptor(m_ios, tcp_endpoint_type(address_v4_type::from_string("0.0.0.0"), port))
, m_stop_listen(false)
, timer_clear_server_list (m_ios , 10000, 10000)
{
m_servers.clear();
if (ios_num < 0){ ios_num = 0 };
//创建IO_SERVICE线程,作为pAsyncServerType的IO工作线程,用于读写TCP流
for (int i = 0; i < ios_num; ++i)
{
io_service_ptr_type pios = boost::make_shared();
work_ptr_type pwork = boost::make_shared(*pios);
m_vec_ios.emplace_back(pios, pwork);
m_thgroup_srv.create_thread(boost::bind(&io_service_type::run, &(*pios)));
}
//创建IO_SERVICE线程,作为CAsyncAcceptor的IO工作线程,用于监听TCP连接
m_pwork = boost::make_shared(m_ios);
boost::thread th1(boost::bind(&io_service_type::run, &m_ios));
//启动定时器,用于清理过期的server连接
timer_clear_server_list.SetCallBack(std::bind(&CAsyncAcceptor::ClearServerList, this));
timer_clear_server_list.Start();
LOG_INFO_GLOG << "server start listen on port=" << port;
}
~CAsyncAcceptor()
{
timer_clear_server_list.Stop();
m_pwork.reset();
m_ios.stop();
for (int i = 0; i < m_vec_ios.size(); ++i)
{
m_vec_ios[i].second.reset();
m_vec_ios[i].first->stop();
}
if (!m_vec_ios.empty())
{
m_thgroup_srv.interrupt_all();
m_thgroup_srv.join_all();
}
}
void ClearServerList()
{
int size_before_remove = m_servers.size();
m_servers.remove_if([](pAsyncServerType & e){return e->is_stopped(); });
int size_after_remove = m_servers.size();
LOG_IF_INFO_GLOG(size_before_remove != size_after_remove) << "size_before_remove="
<< size_before_remove << ";size_after_remove=" << size_after_remove;
if (m_stop_listen == true && size_after_remove < tcp_max_connected_server_num)
{
StartAsyncAccept();
m_stop_listen = false;
LOG_INFO_GLOG << "server restart listen";
}
}
void SetAcceptHandle(std::function handle)
{
m_handle_func = handle;
}
void StartAsyncAccept()
{
static int id = 0;
LOG_IF_FATAL_GLOG(!m_handle_func) << "accept handle is null";
if (m_servers.size() >= tcp_max_connected_server_num && m_stop_listen == false)
{
LOG_INFO_GLOG << "server connection overflow ,stop listen";
m_stop_listen = true;
return;
}
pAsyncServerType psrv;
if (m_vec_ios.empty())
{
psrv = boost::make_shared(m_ios, id);
}
else
{
int size_ios = m_vec_ios.size();
psrv = boost::make_shared(*m_vec_ios[id%size_ios].first, id);
}
id++;
m_acceptor.async_accept(psrv->socket(), [this, psrv](const error_code_type & err_)->void{
if (!err_)
{
m_servers.push_back(psrv);//服务端连接对象加入服务端管理链表
psrv->start_async_read();//server read buf--生产
boost::thread th(boost::bind(m_handle_func, psrv));//server read buf--消费
}
else
{
LOG_INFO_GLOG << "accept error:" << err_.message();
}
StartAsyncAccept();
});
}
};
使用说明