CVM 网络类库
CVM 是一个基于 Linux 的小型网络类库,她主要提供多路分离复用、线程池、内存管理和
数据访问功能,下图是类库的简单分析模型
多路分离复用:是对 Epoll 和 IO 的使用封装,使得使用者可以完全与 Epoll 的细节隔开,
专心于 IO 的处理
线程池:提供一种执行任务的服务,用户只需按照任务接口实现自己任务然后将它放到线
程池即可执行任务
内存管理:实现内存的有效管理,主要有固定大小内存池、简单工厂和复杂工厂
数据访问:实现数据的简单有效访问,主要包括数据库(Mysql)的访问、文件的访问和配置
文件的访问
图 1.0
从图 1.0 不难看出,左上角部分是多路分离复用功能,右上角是线程池,左下角是数据访
问,
右下角是内存管理,下面对一些基础公共类做详细介绍,主要是同步类和 Socket 类。
地址类
在使用底层 Socket 进行网络编程时,对地址的操作可以说会伤透使用者的心,地址结构体
还得分两个,妈的!!老婆有没有两个啊!!还得进行网络字节序和本机字节序的转换处
理,简直能把人逼得有做空中飞人的冲动,所以为了生命能够多姿多彩,还是拿个笼子把
它们装起来,想出来!!行!!得按照哥的规矩来!!!
CVM_Adress 地址类派生于 sockaddr_in,对它进行封装。
首先是初始化,CVM_Adress 提供多种地址的初始化方式,可以通过它的多个构造函数进
行初始化
CVM_Adress(const SOCKADDR& t_Adress);
CVM_Adress(const SOCKADDR_IN& t_Adress);
CVM_Adress(const ULONG t_ulAddr, const USHORT t_ushPort);
CVM_Adress(const char* t_pchIP, const USHORT t_ushPort = 0);
构造函数会帮你分辨好是那个老婆和啥字节序的问题。
除 此 之 外 , 还 提 供 对 Ip 和 端 口 的 设 置 和 获 取 操 作 , CVM_Adress 和
SOCKADDR、SOCKADDR_IN 之间的赋值和转换操作都处理好,
具体代码可以查看源代码 CVM 网络类库\code\Socket\CVM_Adress.h
Socket 类
Socket 编程分为 UDP 编程和 TCP 编程,至于 UDP 和 TCP 之间的区别这里不再详
说 , CVM 中 的 socket 类 有 三 个 类 构 成 : CVM_Socket 、 CVM_TcpSocket 和
CVM_UdpSocket。它们之间的关系人图 1.1
它们都使用CVM_Adress进行地址的处理,CVM_Socket封装socket的一些共同操作,如
关闭、连接、发送接收数据和一些获取本方或对方地址的服务,对于创建需在
CVM_TcpSocket
CVM_UdpSocket 自己的类中现实,一些它们特有的服务也许它们自己搞定,如 TCP 的
监听和接收连接,UDP 的接收发送数据。
它们只是对 socket 进行了简单封装,在这不详细讲述,具体可以查看源代码:
CVM 网络类库\code\\ Socket\ CVM_Socket.h
CVM 网络类库\code\\ Socket\ CVM_TcpSocket.h
CVM 网络类库\code\\ Socket\ CVM_UdpSocket.h
同步类
在网络编程中,线程同步简直就像吃饭一样,所以在 CVM 中也提供了同步类。
CVM_Lock 类,是所有同步类的接口,提供三个服务:
virtual int Lock() = 0; // 获取锁,阻塞直到获得锁
virtual int TryLock() = 0; // 尝试获取锁,不会阻塞,马上返回
virtual int UnLock() = 0; // 释放锁
所有的同步类都需要从 CVM_Lock 派生并实现上述三个纯虚函数。
CVM 暂时只提供了 CVM_Mutex 和 CVM_Semophore 两个同步类(它们的关系如图 1.3)
CVM_Mutex 是对互斥体 Mutex 的封装,对某个资源进行同步保护,可分为快速型、递
归型和错误检测型。
快速型:这种类型也是默认的类型。该线程的行为正如上面所说的。
递归型:如果遇到我们上面所提到的死锁情况,同一线程循环给互斥量上锁,
那么系统将会知道该上锁行为来自同一线程,那么就会同意线程给该
互斥量上锁。
错误检测型:如果该互斥量已经被上锁,那么后续的上锁将会失败而不会阻塞,
Lock()操作将会返回 EDEADLK。
CVM_Semophore 是对信号灯 Semophore 的封装,信号灯其实就是一个计数器,
也 是一 个 整 数 。 每 一 次 调 用 Lock 操作 将 会使 semaphore 值 减一 , 而 如果
semaphore 值已经为 0,则 Lock 操作将会阻塞。每一次调用 UnLock 操作将会使
semaphore 值加一。
不管是 CVM_Semophore 或 CVM_Mutex 都需要先初始化在使用
图1.3
具体可以查看代码:
CVM 网络类库\code\Thread\CVM_Lock.h
CVM 网络类库\code\Thread\CVM_Mutex.h
CVM 网络类库\code\Thread\CVM_Semophore.h
线程类
网络编程中,线程的使用是必不可少的,可以说是网络编程人员必须熟悉的技术,在这不
讲多线程编程,只讲如何封装一个线程类。
C++不像 java 提供有自己的线程类支持,不过在 MFC 或 boost 中还是有提供线程类支持,
只是我个人都不太喜欢那些类,总觉的不爽,也受过一个苦头,记得大三时,用 MFC 的
线程类和 Socket 类写一个 IM 软件,真是搞的我快疯了,一气之下就不用它们了,果断使
用底层线程和 socket,从现在看来当时真是做了一个明智的决定,虽然当时因为对底层线
程和 socket 使用没啥经验,那个软件也做的蛮失败的,但也从中学到的很多,shit!说多了!
还是说线程类吧
一 般 我 都 是 先 定 义 一 个 全 局 函 数 , 再 使 用 线 程 创 建 函 数 (Linux 下 为
pthread_create,windows 为 CreateThread 或_beginthread,建议使用_beginthread),再传入
线程函数地址和线性函数的参数,即可启动一个线程,只是这种方法在 C++中显得不太协
调,还是把他关起来好一点,他娘的!!她要乱跑起来真是让人心惊胆战啊!!上厕所拉
个便都不得安心!!
CVM 的线程类只是简单将线程的创建运行封装起来,有一点 java 线程类的味道。
看一下类的头文件
class CVM_Thread
{
virtual bool Run() = 0;
virtual bool CreateThread(void* t_PtrData = NULL);
bool Close();
static void * BeginThread(void* t_Thread);
pthread_t GetThreadId();
private:
// 线程的ID
pthread_t mv_ThreadId;
};
所有的线程类必须从CVM_Thread派生,且必须重写Run(),因为她是线程类执行函数,线程启动后派
生类的Run()就会被调用,你即可在这执行你想要做的事情,可以操作线程类的成员变量和成员函数。
在这说说线程类的启动步骤:
1、 创建一个线程类对象 如CMy_Thread lv_Thread.
2、 调用线程类的创建函数CreateThread(void* t_PtrData = NULL);,这个函数可以选择在派生类
中重写,并以void*形式传入你自己的需要的参数,但必须在派生类的CreateThread(void*
t_PtrData = NULL); 中显示调用 基类的CreateThread(void* t_PtrData = NULL); 函数 如
CVM_Thread:: CreateThread(); 这样才能启动线程。
3、 在CVM_Thread:: CreateThread中会调用pthread_create来创建真正的线程,线程函数为
CVM_Thread的静态函数BeginThread,传入的参数为线程类的指针,在BeginThread他会
使用线程类指针调用Run()函数。
4、 接下来的事情就看看你怎么执行派生类的Run()函数了!!!!!
具体的情况可以看源代码:
CVM网络类库\code\Thread\CVM_Thread.h
数据访问
数据访问由两部分组成:数据库访问和文件访问
数据库访问:以前我只用过 MSSQL 和 ACCESS ,对于 MySql 可以说没什么了解,上网找
了一些资料安装了 MySql,熟悉了如何使用 MySql 和 C++的 MySql 编程,再从网上偷了一
位仁兄的一个 MySql 封装类,修改一下就变成了小弟的数据库访问类,希望那位仁兄见
谅!!
CVM_MySQLManager 的功能并不多,主要有初始化数据,打开数据库连接,执行 SQL 语句、获取记
录集、关闭数据库等几个常用的操作,下面是一段操作代码实例:
///////////////////////////////////////数据库//////////////////////////////////////////////////////
cout<< "test for MySql" << endl;
// 创建一个数据库访问对象
CVM_MySQLManager lv_SQLManager("localhost", "root", "111", "wolf", 3306);
// 初始化 打开与数据库的连接
lv_SQLManager.InitConnection();
//lv_SQLManager.ExecuteSQL("insert into myclass values(6, 1, 'alan')");
lv_SQLManager.ExecuteSQL("delete from myclass where id = 6");
// 执行SQL语句 并获取记录集
CVM_MYSQL_REG lv_DataList =