概述
模仿python的twisted方式,构建了一个基于C++的Tcp服务器。
一、开发环境的部署
1、操作系统为CentOS,我的版本是7.3.1611
2、安装C++编译器,我的版本是4.8.5
(1)yumlist gcc-c++
(2)yuminstall gcc-c++.x86_64
3、安装libevent网络框架,我的版本是2.1.8
(1)进入解压后的目录,执行
./configure -prefix=/usr
make
make install
(2)检查是否安装成功
ls -al /usr/lib | grep libevent
(3)安装libevent-devel
yum list libevent-devel
yum install –y libevent-devel.x86_64
(4)以下步骤可能是需要的
cp /usr/lib/libevent-2.1.so.6 /usr/lib64
4、安装配置MariaDB数据库,当前版本5.5.56
(1)yuminstall –y mariadb mariadb-server
(2)systemctlstart mariadb
(3)systemctlenable mariadb
(4)初设密码:mysql_secure_installation
--------安装mariadb-devel--------
(5)yumlist mariadb-devel
(6)yuminstall –y mariadb-devel.x86_64
5、下载安装cmake,当前版本3.10.0
进入解压后的目录执行:
./bootstrap
gmake
gmake install
6、执行mariaCpp类库需要用到mariadb-connector的头文件及相关定义,故先安装mariadb-connector,当前版本3.0.2
下载地址:点击打开链接
进入解压后的目录执行:
mkdir build
cd build
cmake ..
make –j8
7、将编译后产生的头文件拷贝到/usr/include/mysql文件夹中
cp ./include/*.h /usr/include/mysql
cp ./build/include/*.h/usr/include/mysql
8、mariaDB的C++库,下载地址是:点击打开链接
将mariaCpp类库中的两个文件夹:mariacpp(包含头文件)、src(包含实现文件)拷贝到项目文件夹。类库中部分编码好像不符合当前c++编译器规范的要求,需做适当改动。
9、Makefile的编写
target_exe: xxx1.cpp xxx2.cpp … xxxn.cpp
g++ xxx1.cpp xxx2.cpp … xxxn.cpp
-o target_exe –levent –Wall
`mysql_config --cflags --libs`
-I/home/liuhui/dbtest #假设mariaCpp存放在这个目录, 就是刚才第8步那两个文件夹的父文件夹
二、说明
我的服务器是为单位机关内部众多的消费机服务的,处理结构模仿 twisted 风格,一个 factory 对象服务于多个 protocol 客户端对象。程序从consumer.cpp开始
#include <libevent/myreactor.h>
int main(void){
CFactory factory;
if(!factory.Init()){
return 1;
}
factory.Run();
return 0;
}
mylibevent.cpp是对libevent做简单包装,便于我们在集中精力于业务层。如不熟悉,可能需要单独对这块做学习,本文不赘述。
myreactor.cpp里定义了factory和protocol,他向下调用libevent网络层、和mysql层(mariacpp),factory对象内包含了一个vector,用来保存众多客户端对象,当服务端需要主动向全部客户端发送消息时,就有用了。
#include <iostream>
#include "myreactor.h"
#include "mylibevent.h"
#include <global.h>
#include <tools.h>
#include <mariacpp/exception.hpp>
#include <mariacpp/uri.hpp>
#include "echo_dev.h"
CFactory::CFactory(){
m_pConn = new MariaCpp::Connection;
m_vProtocol = new std::vector<CProtocol*>;
}
void
CFactory::Release(){
CMyLibEvent::Release();
if(NULL != m_vProtocol){
std::vector<CProtocol*>::iterator it;
for(it=m_vProtocol->begin(); it!=m_vProtocol->end(); it++){
if(NULL != (*it)){
delete (*it);
(*it) = NULL;
}
}
m_vProtocol->clear();
delete m_vProtocol;
m_vProtocol = NULL;
}
if(NULL != m_pConn){
m_pConn->close();
delete m_pConn;
m_pConn = NULL;
}
}
CFactory::~CFactory(){
Release();
}
bool
CFactory::Init(){
try {
m_pConn->connect(MariaCpp::Uri(URI), USER, PASSWD);
m_pConn->set_character_set("utf8");
std::clog << "Connection status: SUCESS" << std::endl;
std::clog << "MySQL Stat: " << m_pConn->stat() << std::endl;
} catch (MariaCpp::Exception &e) {
//std::cerr << e << std::endl;
e.print("connection");
Release();
return false;
}
return CMyLibEvent::Init(this);
}
void
CFactory::AddProtocol(CProtocol* p){
m_vProtocol->push_back(p);
}
void
CFactory::DelProtocol(CProtocol* p){
std::vector<CProtocol*>::iterator it;
for(it=m_vProtocol->begin(); it!=m_vProtocol->end(); it++){
if(NULL != (*it) && p == (*it)){
delete (*it);
(*it) = NULL;
m_vProtocol->erase(it);
break;
}
}
}
void
CFactory::DelProtocol(int nDevNo){
std::vector<CProtocol*>::iterator it;
for(it=m_vProtocol->begin(); it!=m_vProtocol->end(); it++){
if(nDevNo == (*it)->GetDevNo()){
delete (*it);
(*it) = NULL;
m_vProtocol->erase(it);
}
}
}
void
CFactory::Run()const{
CMyLibEvent::Run();
}
CProtocol类的几个重要成员函数
// 对客户端写数据
void
CProtocol::Write(char* buf, int len){
CMyLibEvent::Write(m_pBev, buf, len);
}
客户端首次连接,可以向其要求注册信息,对方回发注册信息后,可以根据规则判断其是否合法。如非法则切断。
void
CProtocol::ConnectionMade(){
// 首次连接,向客户端要求注册
char sz[27] = {0x97, 0x98, 0x00, 0x12, 0x01, 0x01, 0x00, (char)0xA4, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x5A,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00};
Write(sz, 27);
}
接收消息的函数,tcp包一定会存在粘包的问题,这里采用的方法比较简单,不考虑拼合。
void
CProtocol::DataReceived(char* buf, int nLen){
int nIndex = 0;
bool b = true;
while(b){
if(nIndex > nLen - 1){
break;
}
if(0x97 != (u8)buf[nIndex] || 0x98 != (u8)buf[nIndex+1]){
nIndex++;
}
else{
char* req = buf + nIndex;
int nOrdLen = (((u8)req[2]) << 8) + ((u8)req[3]) + 8;
//这里是一些业务验证代码,略过
switch((u8)req[4]){
// 心跳
case(0xFD):{
//todo
nIndex += 8;
break;
}
// 注册信息
case(0x01):{
//todo
nIndex += nOrdLen;
break;
}
// 请求系统时间
case(0xF5):{
//todo
nIndex += nOrdLen;
break;
}
// 请求服务器参数
case(0xF6):{
//todo
nIndex += nOrdLen;
break;
}
// 查询、消费
case(0xF8):{
//todo
nIndex += nOrdLen;
break;
}
// 上传消费记录
case(0xF0):{
//todo
nIndex += nOrdLen;
break;
}
default:{
nIndex++;
break;
}
}
}
}
}
另外还有一些工具类,工具函数,见工程文件点击打开链接
本人QQ: 25468865,加友请注明csdn
最后
以上就是优美小懒猪为你收集整理的一个基于C++的Tcp异步服务器,CentOS7 + MariaDB + libevent的全部内容,希望文章能够帮你解决一个基于C++的Tcp异步服务器,CentOS7 + MariaDB + libevent所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复