最近在研究libuv的代码,觉得如果使用libuv作为开发库的话,缺少一个快速大规模开发的框架,所以自己倒腾了一个----simple_uv。
simple_uv是基于libuv封装跨平台库,旨在降低入门门槛和提高开发效率以及降低开发者人为出错的概率。
simple_uv简化了客户端与服务端的通信,客户端与服务端的所有交互消息都以某一个类的对象回调到上层业务,你不用关注客户端与服务端通信过程中消息的解析以及堆空间的申请与释放等繁琐且易出错的处理。
为了方便的进行多线程程序的开发以及多线程之间高效率的通信,simple_uv实现了一个CUVThread的线程类,类似windows上mfc中的CWinThread,可以响应其他线程发送过来的异步非阻塞消息,CUVThread提供了一个SendUvMessage的函数,可以向任意的CUVThread线程发送消息。
此外,simple_uv还提供了一个CUVThreadMng的类,管理所有的CUVThread线程,你可以在任意线程中调用CUVThreadMng::GetInstance()->SendUvMessage向指定的CUVThread发送消息。
在simple_uv中,对于客户端发到服务端的消息,服务端都有一个对应的响应函数来处理。例如客户端发送了一个CTestMsg类型的消息到服务端,服务端可以在这样一个函数中响应客户端的请求
int CTestGateWay::OnUvMessage(const CTestMsg &msg, TcpClientCtx *pClient)
{
return CUVThreadMng::GetInstance()->SendUvMessage(msg, msg.MSG_ID, SERVER_TEST_CENTER_TYPE);
}
其中TcpClientCtx *pClient是客户端的socket连接句柄,服务端可以通过SendUvMessage(const TYPE& msg, size_t nMsgType, TcpClientCtx *pClient)函数向客户端回传消息。CUVThreadMng::GetInstance()->SendUvMessage可以向任意的基类为CUVThread的线程发送消息。
客户端与服务端同理。
对于Linux上,simple_uv提供了一个Makefile的模板,你只需要在模板中填写src目录,需要生成的文件名,库目录和库名,以及编译所需的特殊宏定义即可。毕竟对于几十或者上百的cpp文件时原生态写Makefile还是比较麻烦且容易出错的(后续提供)。
为了降低入门门槛,simple_uv还提供了一套mysql的封装,你无需懂sql语句也可以做数据库相关的开发工作,你只需要填填对应字段即可完成数据库操作。(后续提供)
simple_uv在网络发包的代码参考了phata(wqvbjhc@gmail.com)在GitHub上的开源代码https://github.com/wqvbjhc/libuv_tcp.git
项目地址
https://github.com/Titanarthas/simple_uv
simple_uv中服务端客户端通信
对于服务端,你需要写一个类继承TCPServer,然后在main函数中启动这个类。
例如,你写了一个叫做CTestGateWay的类,你可以在main函数中这样写
int main(int argc, char** argv)
{
CTestGateWay server;
if(!server.Start("0.0.0.0",12345)) {
fprintf(stdout,"Start Server error:%s\n",server.GetLastErrMsg());
}
while(true) {
Sleep(10);
}
return 0;
}
这样服务端就算起来了,客户端可以连接12345端口。
而对于客户端发过来的消息,你需要实现一个函数并且将该消息的消息号与该函数绑定来处理。
比如客户端上来一个类型为CTestMsg的消息,你可以这样完成。
1,声明一个处理函数(注意,响应函数中是class的类型不同,函数名必须是OnUvMessage,且第二个参数必须是TcpClientCtx *类型)
int OnUvMessage(const CTestMsg &msg, TcpClientCtx *pClient);
2,绑定消息响应(填写消息号和对应类型)
`BEGIN_UV_BIND
UV_BIND(CTestMsg::MSG_ID, CTestMsg)
END_UV_BIND(TCPServer)`
3,在响应函数中实现你收到消息后想做的事。
`int CTestGateWay::OnUvMessage(const CTestMsg &msg, TcpClientCtx *pClient)
{
return CUVThreadMng::GetInstance()->SendUvMessage(msg, msg.MSG_ID, SERVER_TEST_CENTER_TYPE);
}
`
客户端响应消息同理,客户端的创建可以参考例子代码。
simple_uv中多线程编程
如果你觉得服务端单线程已经不能满足客户端的请求,或者有其他耗时任务需要多线程处理的时候,你可以写一个继承CUVThread的类来完成你的需求。
如果你是想实现的是一个同步阻塞的线程,重写Run函数即可。
如果你的需求是要一个带有消息响应的线程,你可以按照如下的方法来完成它。
1,构造函数中记得填上该线程的编号(没编号无法接收其他线程发送过来的消息),例如
`CTestServerCenter::CTestServerCenter(void)
: CUVThread(SERVER_TEST_CENTER_TYPE)
{
}`
2,声明需要处理的消息的响应函数,例如
void OnUvThreadMessage(CTestMsg msg, unsigned int nSrcAddr);
3,绑定消息响应,例如
`BEGIN_UV_THREAD_BIND
UV_THREAD_BIND(CTestMsg::MSG_ID, CTestMsg)
END_UV_THREAD_BIND(CUVThread)`
4,完成消息响应函数,例如
`void CTestServerCenter::OnUvThreadMessage( CTestMsg msg, unsigned int nSrcAddr )
{
msg.m_nSessionID++;
}
`
如果你在CUVThread类的线程中向其他任意CUVThread线程发送消息的时候,你只需要调用this->SendUvMessage()即可,像这样
`this->SendUvMessage(msg, msg.MSG_ID, SERVER_TEST_CENTER_TYPE);`
现在,假如你已完成线程类的实现,你还需要做一件事--让你的线程Run起来。你可以在任何你想把线程创建起来的地方贴上类似下面的代码即可
`CTestServerCenter serverCenter;
serverCenter.Start();`