刚出差回来,发现我们又有好久都没有更新新东西了,好吧,本来打算好好休息的。但是想想趁着休息,简单给大家说一些新东西——boost的asio。
在很久以前大家就比较对boost的内容比较感兴趣,但是当时因为考虑到我们的标准库都还没有和大家说完,也就一拖再拖,那么,既然标准库以及一些C++的常规知识都和大家说了,现在也该是我们聊一些新鲜内容的时候了。
在C++网络编程的模块里面,asio不算最有名的,但也基本是够用的,当然重要是还是因为他的简单,如果大家有兴趣,网上一搜boost的asio,相信能够找到大把的资料,所以这里我们用一个例子给大家演示一下asio的网络编程,那么今天我们要说的是一个服务端的编程(先说完服务端再来说客户端,因为相对服务端来说,客户端就相对简单得多),这个服务器的功能不多,这是因为公司项目而生的,所以都只有一些对应的功能,但用来作为学习用的demo已经足够。
在说网络编程之前,我们要先了解一个东西——定时器(deadline_time),要使用这个东西我们需要包含<boost/data_time/posix_time/posix_time.hpp>头文件,同时我们还需要引入io_service,所以还需要添加一个<boost/asio.hpp>头文件,他们都在名字空间boost::asio中,这里简单的演示一下这个东西的用法,让大家有个眼熟,因为在接下来的客户端编程中,我们会将他运用在异步等待上面。
在异步定时中deadline_time需要一个等待回调函数,这个函数的原型大概就是这样:
//========================================================
void(LPFUN*)(const boost::system::error_code&);
//========================================================
从参数我们可以看得出,如果发生错误的话,那么会有相关的操作系统的错误码发生,所以我们可以检测这个参数以便知道是否有错误发生。我们还是来个简单的demo:
//=================================================================
#include <boost/asio>
#include <boost/data_time/posix_time/posix_time.hpp>
#include <iostream>
//---------------------------
// 异步等待回调函数
//---------------------------
void waitHandle(const boost::system::error_code& e){
if(e){
std::cerr<<"have a error happen..."<<std::endl;
}
else{
std::cerr<<"wait finish..."<<std::endl;
}
}
int main(){
using namespace boost::asio;
io_service io;
deadline_time t(io,posix_time_seconds(5));
t.async_wait(waitHandle);
std::cerr<<"waiting .... <<std::endl;
io.run();
return 0;
}
//==============================================
运行程序,我们会看到在打印waiting .... 之后大约5s的时候才会打印出wait finish....看到这个结果后大家是不是对他的作用有了一些了解了呢?
嗯,给大家演示了一下定时器的异步等待功能还有一个用处就是让大家了解一下asio中的异步操作所需要的一些东西:
1. io_service需要开启消息循环模式(简单点说也就是需要调用io_service::run)
2. 需要一个异步操作句柄(例如上面的waitHandle)
在对asio的异步模型有所了解之后那么我们就可以看看怎么使用boost的asio来写一个简单的服务器了:
//======================================================
#pragma once
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <memory>
#include <functional>
#include <vector>
#include <algorithm>
#include <thread>
#include <boost/algorithm/string.hpp>
#define MAX_DATA 8192000 //单次最大发送8M数据
using namespace boost;
using namespace boost::asio;
typedef std::shared_ptr<ip::tcp::socket> Sock_Ptr;
typedef std::shared_ptr<std::vector<char>> Buffer;
typedef std::function<void(const std::string,Sock_Ptr)> Msg_Proc_Fun;
class HService
{
public:
HService(unsigned port = 12000);
virtual ~HService();
//服务端可以是单例
static std::shared_ptr<HService> Instance(){
static std::shared_ptr<HService> _ptr(new HService);
return _ptr;
}
//同步操作
int Send(const std::string& str,Sock_Ptr sock);
int Read(std::string& str, Sock_Ptr sock);
//异步操作
void PostRecive(Sock_Ptr sock);
void PostSend(const std::string& str,Sock_Ptr sock);
//开启线程,异步循环处理消息
void run();
//=============================================
// 将自己的消息处理函数绑定过来处理消息
//=============================================
void bindProcMsgFun(Msg_Proc_Fun fun);
protected:
void start();
//================================================
// 异步接收回调函数
//================================================
void accept_handle(const boost::system::error_code& e,Sock_Ptr sock);
//================================================
// 异步发送回调函数
//================================================
void Send_handle(const boost::system::error_code& e, Sock_Ptr sock, std::string &str);
//================================================
// 异步读取回调函数
//================================================
void Read_handle(const boost::system::error_code& e,Sock_Ptr sock,Buffer rdbuffer);
void startthread();
private:
io_service m_ios;
unsigned n_port; //开启服务端口
ip::tcp::endpoint m_port; //服务器的端口,需要一个ip和端口构造
ip::tcp::acceptor m_acceptor;//接收器,所有想要访问的该服务器的socket都需要他来处理
std::thread m_thread;
Msg_Proc_Fun m_msg_recall_fun; //只处理网络接收到的信息
std::shared_ptr<io_service::work> m_work;
};
//----------------------------------------
#include "HService.h"
HService::HService(unsigned port) :
n_port(port), m_port(ip::tcp::v4(),
n_port), m_msg_proc_fun(nullptr), m_acceptor(m_ios, m_port),
m_msg_recall_fun(nullptr)
{
m_work = std::shared_ptr<boost::asio::io_service::work>(new boost::asio::io_service::work(m_ios));
start();
}
HService::~HService()
{
if (m_thread.joinable())
m_thread.join();
}
void HService::start()
{
Sock_Ptr sock = Sock_Ptr(new ip::tcp::socket(m_ios));
m_acceptor.async_accept(*sock, std::bind(&HService::accept_handle, this,
std::placeholders::_1, sock));
}
int HService::Send(const std::string &str, Sock_Ptr sock)
{
if (str.empty() || sock._Expired())
return 0;
static int count = 0;
boost::system::error_code e;
int ret = sock->send(buffer(&str[0], str.length()), 0, e);
if (e){
if (count == 5){
if (m_msg_proc_fun)
m_msg_proc_fun("消息发送失败,请检查网络是否出现异常!!!",sock);
std::cout << "消息发送失败!!!!!\n";
sock->close(); //关闭连接
count = 0;
return -1;
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("消息发送失败,将进行重新发送......", sock);
std::cout << "消息发送失败,将进行重新发送......\n";
//等待1秒,重发消息
asio::deadline_timer t(m_ios, posix_time::seconds(1));
t.wait();
++count;
Send(str, sock);
}
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("消息发送成功!!!!!", sock);
std::cout << "消息发送成功!!!!\n";
count = 0;
return ret;
}
return -1;
}
int HService::Read(std::string &str, Sock_Ptr sock)
{
Buffer rdbuffer = Buffer(new std::vector<char>(409600, 0));
boost::system::error_code e;
int ret = sock->receive(buffer(*rdbuffer), 0, e);
if (e){
if (m_msg_proc_fun)
m_msg_proc_fun("接收消息失败!!!!!", sock);
std::cout << "接收消息失败!!!!!" << std::endl;
return -1;
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("接收消息成功!!!!!", sock);
std::cout << "接收消息成功!!!!!" << std::endl;
str = std::string(&(*rdbuffer)[0]);
if (m_msg_proc_fun)
m_msg_proc_fun(str, sock);
std::cout << str << std::endl;
return ret;
}
return -1;
}
void HService::PostRecive(hlw::Sock_Ptr sock)
{
Buffer rdbuffer = Buffer(new std::vector<char>(409600, 0));
sock->async_read_some(buffer(*rdbuffer), std::bind(&HService::Read_handle, this,
std::placeholders::_1, sock, rdbuffer));
}
void HService::PostSend(const std::string &str, hlw::Sock_Ptr sock)
{
if (str.empty())
return;
sock->async_send(buffer(&str[0], str.length()), std::bind(&HService::Send_handle, this,
std::placeholders::_1, sock, str));
}
void HService::run()
{
m_thread = std::thread(&HService::startthread, this);
}
void HService::bindProcMsgFun(hlw::Msg_Proc_Fun fun)
{
m_msg_recall_fun = fun;
}
void HService::accept_handle(const boost::system::error_code &e, hlw::Sock_Ptr sock)
{
if (e){
if (m_msg_proc_fun)
m_msg_proc_fun("错误:接收客户端连接失败!!!!!", sock);
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("客户连接成功!!!", sock);
std::cout << "Client:\n" << sock->remote_endpoint().address() << std::endl;
PostRecive(sock);
start();
}
}
void HService::Send_handle(const boost::system::error_code &e
, Sock_Ptr sock, std::string& str)
{
static int count = 0;
if (e){
if (count == 5){
sock->close();
count = 0;
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("发送消息失败,正在重新发送.....", sock);
std::cout << "发送消息失败......\n";
//等待1秒,重发消息
asio::deadline_timer t(m_ios, posix_time::seconds(1));
t.wait();
//重新发送
++count;
PostSend(str, sock);
}
}
else{
if (m_msg_proc_fun)
m_msg_proc_fun("发送消息成功!!!!", sock);
str.clear();
{
std::string temp;
std::swap(str, temp);
}
std::cout << "\n发送消息成功!!!!" << std::endl;
count = 0;
//投递一个接收
if (sock.use_count() <= 5)
PostRecive(sock);
}
}
void HService::Read_handle(const boost::system::error_code &e,
hlw::Sock_Ptr sock, Buffer rdbuffer)
{
if (e){
std::cout << "消息接收失败!!!!!\n";
}
else{
std::string str = std::string(&(*rdbuffer)[0]);
PostRecive(sock);
if (m_msg_recall_fun)
m_msg_recall_fun(str, sock);
}
}
void HService::startthread()
{
for (int i = 0; i < 8; ++i){
std::thread th([&](){m_ios.run(); });
th.detach();
}
}
//=======================================================
关于异步操作有一个特点,那就是无论何时我们要保证有一个接收操作在执行,因为他不同于同步操作,同步操作的话只有想要的时候去接收直到接收完成后才完成操作,而异步是由操作系统负责,所以你永远不知道你的信息什么时候会来,所以我们需要保证无论何时有信息来我们都要能够接收和处理,也就是大家看到的为什么我们总是随时保持着PostRecive这个操作的原因。
ok,这个服务端算是完成,对了,C++不同于python这些脚本语言,所以我不能三句两句就给大家搞出一个东西出来,我所能够做的就是演示C++一些模块的用法...
由于这个模块需要配合客户端的使用,所以等我们将客户端的程序写出来后再一起看用法....