(四)基于BOOST ASIO实现的异步TCP客户端与服务端

(四)基于BOOST ASIO实现的异步TCP客户端与服务端

代码

//asio_socket.h
#pragma once
#include <queue>
#include <mutex>
#include <string>
#include <vector>
#include <atomic>
#include <map>
#include <unordered_map>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <assert.h>
#include <condition_variable>
#include <iostream>
#include "glog/init_glog.h"

typedef boost::asio::io_service								io_service_type;
typedef boost::shared_ptr<io_service_type>					io_service_ptr_type;
typedef boost::asio::ip::tcp::resolver						tcp_resolver_type;
typedef boost::asio::ip::udp::resolver						udp_resolver_type;
typedef boost::asio::ip::tcp::socket						tcp_socket_type;
typedef boost::asio::ip::udp::socket						udp_socket_type;
typedef boost::shared_ptr<tcp_socket_type>					tcp_socket_ptr_type;
typedef boost::shared_ptr<udp_socket_type>					udp_socket_ptr_type;
typedef boost::asio::ip::tcp::endpoint						tcp_endpoint_type;
typedef boost::asio::ip::udp::endpoint						udp_endpoint_type;
typedef boost::asio::ip::address							address_type;
typedef boost::asio::ip::address_v4							address_v4_type;
typedef boost::asio::ip::address_v6							address_v6_type;
typedef boost::asio::io_service::work						work_type;
typedef boost::shared_ptr<work_type>						work_ptr_type;
typedef boost::asio::ip::tcp::acceptor						tcp_acceptor_type;
typedef boost::shared_ptr<tcp_acceptor_type>				tcp_acceptor_ptr_type;
typedef boost::asio::deadline_timer							dtimer_type;
typedef boost::system::error_code							error_code_type;
typedef std::vector<char>									buffer_type;

#define TCP_V4												boost::asio::ip::tcp::v4()
#define TCP_V6												boost::asio::ip::tcp::v6()
#define UDP_V4												boost::asio::ip::udp::v4()
#define UDP_V6												boost::asio::ip::udp::v6()
#define TCP_V4_PROTOCOL										boost::asio::ip::tcp::v4().protocol()
#define TCP_V6_PROTOCOL										boost::asio::ip::tcp::v6().protocol()
#define UDP_V4_PROTOCOL										boost::asio::ip::udp::v4().protocol()
#define UDP_V6_PROTOCOL										boost::asio::ip::udp::v6().protocol()

#define BOOST_SLEEP_S(x)									(boost::this_thread::sleep(boost::posix_time::seconds(x)))
#define BOOST_SLEEP_MS(x)									(boost::this_thread::sleep(boost::posix_time::milliseconds(x)))


const size_t tcp_max_readbuf_size = 2000;
const size_t tcp_max_writebuf_size = 2000;
const size_t udp_max_sendbuf_size = 2000;
const size_t udp_max_recvbuf_size = 2000;
const int tcp_max_readbuf_raw_size = 65536;
const int tcp_max_msg_length = 65536;
const int udp_max_msg_length = 65536;
const int tcp_max_connected_server_num = 2;// 64;//服务端最大连接数
const int tcp_server_read_timeout = 15;//服务端连接读取超时时间,若长时间没有心跳将断开连接
const int INFIT_TIME = 0x7FFFFFFF;
//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<bool> m_stopped;
	tcp_endpoint_type m_ep;
	tcp_socket_type m_sock;

	std::mutex mut_readbuf;
	std::mutex mut_write_sock;
	std::queue<string> 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 "<<ip<<":"<<port;
	}

	~CAsyncClient()
	{
		m_work.~work();
		cv_readbuf_notempty.notify_all();
		close_socket();
		while (!m_ios.stopped())
		{
			BOOST_SLEEP_MS(500);
		}
		LOG_INFO_GLOG << m_id << "stop client";
	}

	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<std::mutex> 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<std::mutex> 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<std::mutex> 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<CAsyncClient>				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<string, int>		TMSG;//消息和超时时间
private:
	string m_id;
	std::atomic<bool> m_stopped;
	tcp_endpoint_type m_ep;
	tcp_socket_type m_sock;
	std::mutex mut_readbuf;
	std::mutex mut_write_sock;
	std::queue<string> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<CAsyncServer>				pAsyncServerType;

class CAsyncAcceptor
{
private:
	io_service_type m_ios; //acceptor的监听IOS
	tcp_acceptor_type m_acceptor;
	std::list<pAsyncServerType> m_servers;
	std::function<void(pAsyncServerType &)> m_handle_func;
	work_ptr_type m_pwork;
	boost::thread_group m_thgroup_srv;
	std::vector<std::pair<io_service_ptr_type, work_ptr_type>> m_vec_ios;//供服务端连接套接字使用
	std::atomic<bool> 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<io_service_type>();
			work_ptr_type pwork = boost::make_shared<work_type>(*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<work_type>(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<void(pAsyncServerType &)> 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<CAsyncServer>(m_ios, id);
		}
		else
		{
			int size_ios = m_vec_ios.size();
			psrv = boost::make_shared<CAsyncServer>(*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();
		});
	}
};

使用说明

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值