mdk开源服务器引擎(更新V1.67 2013.10.29)+群号
本帖最后由 serverdev2012 于 2013-10-29 16:31 编辑[*]起因
我这个人有一个不好的习惯——作为一个Coder就是不喜欢读代码
在我的意识里,一个好的代码库应该是足够稳定的,稳定到不需要使用者为了解决问题而去读里面的代码。
当然出于学习等目的,读代码是不可避免的
所以我总希望能有一个轻量,轻量,再轻量的开发库就好了,于是萌生了开发一个微量级服务器引擎的想法,
于是mdk(Micro-Development-Kit)微量级开发工具包,就是这套框加的名字
[*]mdk做了什么事情
服务器程序员开发最关心的其实只有3件事情,新连接到达,数据到达,连接断开。
mdk就是基于以上3个主要业务,为服务器设计了OnConnect() OnMsg() OnClose()3个主要回调方法,用户的主要的工作就是实现这3个方法,所以我将这种开发模式命名为面向业务的开发。
用户不再关心底层各种繁杂的处理,比如线程模型,各种同步,内部对象生命周期,各种缓冲只要专心写业务就好
整个框架代码大约5000行,希望不会给大家造成阅读负担,
希望这款服务器能给大家带来更多便利。
[*]项目相关url
下载地址(使用github下载):https://github.com/huoyu820125/Micro-Development-Kit
为方便没有github的朋友,从V1.25版本开始,新增google下载地址:http://code.google.com/p/micro-development-kit/
文档地址(wiki):https://github.com/huoyu820125/Micro-Development-Kit/wiki
OSChina博客:http://my.oschina.net/u/732357/blog
脑图(.mm文件):使用freemind_1.0.0软件打开
[*]开发环境
windows下,使用速度最快的经典VC++6.0,追求速度的朋友有服了
经查VC6编译器优化后的代码存在bug,请大家使用更高版本的编译器,具使用者反映,目前VS2008 2010均可正常,2003 2005未收到反馈
具体问题可以入群下载mdk VC6崩溃原因说明.rar
linux下直接makefile编译
[*]更新历史
V1.67(2013.10.29)
1.服务器框架改动
1.1增加表示连接数据的基础类HostData
用户直接从HostData派生自己的数据类型即可
通过NetHost::SetData()接口将数据与连接关联
具体使用方法参考头文件
1.2NetHost的IO接口优化
数据长度,从原来的unsigned short扩大到unsigned int
2.优化Logger日志类
2.1增加3个接口
SetMaxLogSize()日志最多保存这个大小的内容
SetMaxExistDay()日志最多存在这么多天
SetLogName()日至名字,会产生一个日志名的文件夹
2.1写各种类型日志接口,统一为2个
Info()输出日志
StreamInfo()输出流
3.扩展ConfigFile配置文件类
增加对Section的支持
使用范例
配置文件内容
ip = 192.168.0.1
port = 8888
ConfigFile cfg( "./test.cfg" );
string ip = cfg["ser config"]["ip"];
cfg["ser config"]["ip"] = "127.0.0.1";
cfg["ser config"]["port"] = 8080;//可以直接使用char*、string、int、float等进行赋值
cfg.save();
4.ThreadPool线程池增加1个接口,GetTaskCount取得当前任务数
5.Socket类
5.1优化Connect()接口,可以直接传递域名进行连接
5.2增加static接口HostName2IP(),将域名转换成IP
6.mapi.h增加几个公用api
6.1mdk_assert代替assert()
6.2GetFileSize
6.3GetCUPNumber
6.4CurThreadId
6.5mdk_Date
7.优化一些造成编译警告的代码
V1.6.5(2013.07.05)
解决windows下服务器退出时,IOCP阻塞问题,实现IOCPMonitor虚函数,解决
bool IOCPMonitor::Stop()
{
#ifdef WIN32
memset( &m_stopOverlapped.m_overlapped, 0, sizeof(OVERLAPPED) );
m_stopOverlapped.m_wsaBuffer.buf = NULL;
m_stopOverlapped.m_wsaBuffer.len = 0;
m_stopOverlapped.m_overlapped.Internal = 0;
m_stopOverlapped.sock = 0;
m_stopOverlapped.completiontype = IOCPMonitor::close;
::PostQueuedCompletionStatus(m_hCompletPort, 0, 0, (OVERLAPPED*)&m_stopOverlapped );
#endif
return true;
}
V1.6.4(2013.05.08)解决在onconnect中主动close时崩溃问题
1.NetEngine::ConnectWorker感谢群友明亮发现此问题
pConnect->Release();//使用完毕释放共享对象
移动到最后return之前,否则如果在onconnect里面关闭连接,在MonitorConnect之前
对象有可能被删除,MonitorConnect访问null指针
2.NetEngine::ConnectWorker
MonitorConnect之前加上条件true == pConnect->m_bConnect
避免在onconnect里面关闭连接后,还将连接加入监听,触发多余的OnMsg
3.threadpool::Stop 没有清空m_tasks
4.threadpool::Stop 总是等到3秒超时才关闭
5.NetEngine::Main里面sleep,使用信号代替,在stop时可以立刻停止线程
提高stop响应速度
6.NetHost::operator=
AtomAdd(&obj.m_pConnect->m_useCount, 1);
语句增加条件检查if ( NULL != obj.m_pConnect )
以支持像下面这样主动释放对象
nethost nullobj;
host = nullobj;//主动释放host
V1.6.2(2013.03.26)解决gcc4.7.2 4.7.3缺少头文件问题
1.Socket.h加上linux头文件 include<unistd.h>感谢群友YOKO提供头文件包含需求
2.IOCP初始化有1处遗忘初始化,该漏洞暂未引发bug,CreateIoCompletionPort的最后1个参数忘记初始化,cpu数量,目前为自动获取机器cpu数量,感谢群友明亮提出
V1.61(2013.03.01) 优化Stop操作
1.WSAStartup与WSACleanup的调用从Start与Stop里面移出,到构造函数与析构函数
2.m_pConnectPoll的释放,从stop里面移到析构中
3.调整NetServer::Stop线程的停止顺序,将m_mainThread线程的停止提前到NetEngine::Stop()之前
V1.60(2013.02.22) 健壮性强化
1.ConfigFile 配置项未初始化,导致IsNull()方法不能返回正确结果
2.NetEngine::Start()强化内部检查
V1.59(2013.02.01)
1.完善Socket::Send(),处理缓冲满时返回实际发送0byte(感谢明亮提出)
2.完善Thread::Stop(),将线程句柄=NULL,只针对windows,以免析构函数再次Stop()(感谢董文嘉提出)
3.mdk引擎发现连接产生时,将socket设置为非阻塞模式,指针windows,以免send操作阻塞
4.mdk停止时主线程停止超时时间是3毫秒,修正为3000
V1.58(2013.01.24)
1.linux下使用Connect()接口产生的Socket未设置成非阻塞,导致epoll线程被recv方法阻塞,无法接收数据,已修正(感谢北极星同学测试发现此bug)
2.windows下 Signal类析构时没有CloseHandle()(感谢明亮同学发现此问题)
3.NetServer::main()注释修改为后台业务线程
4.STNetServer::main()注释修改为后台业务线程
V1.56(2013.01.14)
修正1对n lock free队列bug
bug位置 Queue.cpp
1.
/*
只有在NPush并发情况下,因Push无序完成,第一个位置的Push未完成,
后面的Push就先完成提示有数据,导致这里检查第一个位置时,发现没有数据可读
因为该类只允许1对N,必然是单线程Pop(),所以条件内m_pop不需要并发控制
*/
if ( m_queue.IsEmpty )
{
m_pop--;
AtomAdd(&m_nReadAbleCount,1);
return NULL; //忘记这一句return NULL,对引擎的影响:无,因为只有在 1pop对n push情况下才会进入此分支,而引擎只使用了1 push对n pop的模式
}
2.Push() Pop()以上检查条件中m_nWriteAbleCount m_nReadAbleCount的增加没有使用原子操作做并发控制,实际应该要原子操作。对引擎的影响:linux版本有可能造成遗漏通知,win版未使用Queue类
3.相应的休整了以上条件的注释,更容易理解一点
V1.55(2013.01.12)
1.53心跳检查条件没有bug,因调试时连接的lib版本问题,误认为是bug
修正回来,1.54版本废除
V1.54(2013.01.11)
1.心跳代码检查优化出一个bug,修正
V1.53(2013.01.11)
1.ConfigFile类,增加IsNull方法,检查一个配置值是否被配置
2.Logger类删除构造函数中多余的参数
3.mdk网络引擎自动重连的定时器代码优化
4.NetHost::ID() NetConnect=NULL访问空指针,该问题只有用户自己创建NetHost对象时,才会出现,因为用户创建时是没有做初始化的。
引擎的OnXXX()返回给的NetHost对象,ID()是保证有效的
V1.52(2013.01.05)
1.优化内存池性能,初始化时减小分组大小,使遍历次数减少,感谢北极星提供的性能检测数据
2.删除NetHost::GetLength()方法,对用户无用,且容易误导用户
3.相应的修改文档,example
V1.51(2012.12.27)
1.优化不同2个不同连接,一个连接断开,另外一个连接进入的时序
将NetEngine::CloseConnect()中的pConnect->GetSocket()->Close()调用
推迟到CloseWorker()中,在m_pNetServer->OnCloseConnect()之后执行
A.首先推迟Close的目的
在OnCloseConnect()完成前,也就是业务层完成连接断开业务前
不让系统回收socket的句柄,再利用
以避免发生如下情况。
比如用户在业务层(NetServer派生类)中创建map<int,NetHost>类型host列表
在NetServer::OnConnect()时加入
在NetServer::OnClose())时删除
如果在这里就执行关闭socket(假设句柄为100)
业务层NetServer::OnClose将在之后得到通知,
如果这时又有新连接进来,则系统会从新使用100作为句柄分配给新连接。
由于是多线程并发,所以可能在NetServer::OnClose之前,先执行NetServer::OnConnect()
由于NetServer::OnClose还没有完成,100这个key依旧存在于用户创建的map中,
导致NetServer::OnConnect()中的插入操作失败
因此,用户需要准备一个wait_insert列队,在OnConnect()中insert失败时,
需要将对象保存到wait_insert列队,并终止OnConnect()业务逻辑
在OnClose中删除对象后,用对象的key到wait_insert列队中检查,
找到匹配的对象再insert,然后继续执行OnConnect的后续业务,
OnConnect业务逻辑才算完成
1.代码上非常麻烦
2.破坏了功能内聚,OnConnect()与OnClose()逻辑被迫耦合在一起
B.再分析推迟Close有没有其它副作用
问题1:由于连接没有关闭,在server端主动close时,连接状态实际还是正常的,
如果client不停地发送数据,会不会导致OnMsg线程一直接收不完数据,
让OnClose没机会执行?
答:不会,因为m_bConnect标志被设置为false了,而OnMsg是在MsgWorker()中被循环调用,
每次循环都会检查m_bConnect标志,所以即使还有数据可接收,OnMsg也会被终止
V1.50(2012.12.26)
1.解决gcc4.4.6编译问题,原因1.32版中新增的ShareMemory类用到memcpy系列字符串api,在gcc4.4.6下找不到头文件
2.整理example代码,删除无用代码
V1.35(2012.12.24)
1.解决Bug 连接断开后,自动重连没有效果
2.NetServer STNetServer类补上重连定时设置接口
3.ConfigFile类写入顺序与读入顺序保持一至
V1.34 (2012.12.14)
主要是为了方便windows下VS系列使用者编译,做了相关目录的修正 包含头文件修正为相对路径,
编译mdk lib库不再需要在VC下再设置头文件搜索目录但其它项目使用mdk.lib依旧需要设置mdk头文件目录,
或包含时指定目录
V1.33(2012.12.12)
加强ConfigFile功能
直接存取int char short int32 float double等基本类型
V1.32 (2012.12.10)
1.将代码中实现的通用api整理到一起mapi.*
2.补正VC工程,新增单线程版代码没有编译进lib
3.相应更新文档
4.增加工具类共享内存ShareMemory.*
5.配置文件类ConfigFile.*更加傻瓜式,析构时自动保存修改
V1.30 (2012.12.04)
1.主要是为实现nosql需要单线程的io模型,于是新增一个单线程模型服务器引擎匡架的
基类为STNetServer与NetServer接口完全一样
事件响应返回host类型为STNetHost,与NetHost接口完全一样
2. 1.25之后小更新
1.修正了Socket::Accept在windows下的失败bug
2.发送部分io吞吐能力忘记跟随recv一起扩大,不扩大也没关系,就是原来的低吞吐,已扩大
V1.25 (2012.11.30)
主要优化了io吞吐能力
1.每次io吞吐量从256byte提升至8k,就是IOBufferBlock.h中的宏#define BUFBLOCK_SIZE 8192,BUFBLOCK_SIZE是一块连续内存的最大尺寸,所以一次recv最大可接收的数据受此宏定义限制。
可根据需求自行修改,注意BUFBLOCK_SIZE很大时,请适当调小IOBufferBlock::ms_pMemoryPool初始化内存数量,目前是10000块,也就是8万k大小的内存池
2.linux下每次io从4k提升至1M
3.NetEngine::HeartMonitor()小优化
V1.24 (2012.11.29)
首先最近几次更新,要感谢来至OSChina.net的Fenlog积极提出的相关意见
1.删除被注释掉的无用代码(所谓的僵尸代码)
2.消除class NetServer内存泄露,~NetServer()时没有释放NetEngine对象
还有一个地方的内存没有释放,就是以下红色的全局变量,而且是private的,程序退出就会释放没有关系的,如果实在不爽,可以自行修改为public或增加static释放方法,在程序退出的地方,主动释放一下
class IOBufferBlock
{
friend class IOBuffer;
//////////////////////////////////////////////////////////////////////////
//使用自己的内存管理
private:
//内存池,在这里为该类型的对象分配内存
static MemoryPool* ms_pMemoryPool;
//内存池线程安全锁
static Mutex* ms_pPoolMutex;
V1.23 (2012.11.21更新)
修正了接收数据的一切边界情况
1.nethost::recv第2个参数传递0
具体现象:由于没有读取数据,缓冲状态一直为可读,导致不停的触发OnMsg,修正后如果recv传递0则返回false,且不再触发onmsg,除非有新数据到达修正:IOBuffer::ReadData(),第一行参数检查,0 > uLength 修正为0 >= uLength,读取0长度也返回不可读
2.nethost::recv刚好将缓冲中数据全部读出
具体现象:iobuffer中已经没有数据可读,但onmsg会再被触发1次,数据长度为0修正:NetConnect::IsReadAble()方法,状态为可读时,多检查一下iobuffer的长度,如果<=0则返回不可读
V1.22r (2012.11.21更新)
修正Bug:VC编译Release版本启动崩溃
Bug原因是Executor类中调用函数指针的汇编代码在release下不能正确定位函数地址,导致访问非法地址
修正后所有需要经过Executor远程调用的类成员函数(例如传递给Thread.Run(),ThreadPool::Accept(),Task::Accept()的成员函数),需将函数的调用规则申明为RemoteCall
如:void* RemoteCall Fun(void*)
V1.21(2012.11.08更新)
框架优化1处,接口增加2个
1.对于框架的优化,保证OnConnect OnMsg OnClose完全序列化,这要感谢野火与Connor提出的宝贵建议
2.NetHost增加GetAddress和GetServerAddress方法
用于得连接双方IP 端口信息
V1.20便利性的优化
1.修正1.10版本中编译Signal.cpp时,include atom.h文件名大小写不可识别问题
2.优化服务器引擎NetHost的使用删除了Hold()Free()方法,取而代之的是复制构造函数,=运算符,析构函数来自动化管理引用计数,
用起来更加方便,让用户完全不用关心对象使用中的生命周期问题
3.优化服务器网络事件通知顺序1.10版本中同一个连接上OnConnect与OnMsg是无序并发的优化后在同一个连接上,确保了OnConnect完成之前,不会有该连接的OnMsg发生
4.相应的修正example与文档
5.mdk类库,增加智能指针类SharedPtr
6.mdk类库,Thread在windows下stop时没有CloseHandle()。(前几天群里看到讨论CloseHandle()问题,帮我发现到一个不足点,于是补上呵呵)
V1.10github上发布以来第一个大版本
1.各方面已经调整稳定,为将来写wiki有针对性,设置一个大版本
2.增加了服务器引擎接口文档.docx,方便喜欢看文档的朋友
3.修正NetServer.h NetHost.h方法注释,与增加了服务器引擎接口文档.docx完全一样,方便喜欢直接看代码的朋友
4.修正NetHost关于Group的方法为线程安全的
5.修正心跳检查机制默认为不检查,与NetServer::SetHeartTime()的注释匹配,以免用户被注释误导
最后水平有限,希望大家多提意见,如果发现什么bug或有什么问题,欢迎指出,我一定尽快答复
我的QQ49038554
QQ群号245934320
不错,支持楼主的精神 djoin 发表于 2012-11-8 10:11 static/image/common/back.gif
不错,支持楼主的精神
谢谢支持,有什么问题,可以直接问我 支持开源,支持楼主! to serverdev2012:
请放个下载的链接吧,许多人都不熟悉git, 我下了个git windows客户端,注册了个账户,但在客户端上登录就报密码错误,可能是注册时用的是XP IE8, git不支持IE8. 请直接放个链接或放在google上。 谢谢! eGoldenBright 发表于 2012-11-18 14:26 static/image/common/back.gif
to serverdev2012:
请放个下载的链接吧,许多人都不熟悉git, 我下了个git windows客户端,注册了个账户 ...
兄弟,谢谢你的建议,我也感觉github太麻烦,不过这几天我尝试了很多次,google一直上不去,可能最近18大的关系
你可以加我Q49038554,我直接发你一份,
等google好了,我再传一份 eGoldenBright 发表于 2012-11-18 14:26 static/image/common/back.gif
to serverdev2012:
请放个下载的链接吧,许多人都不熟悉git, 我下了个git windows客户端,注册了个账户 ...
兄弟,google我弄好了
下载地址:http://code.google.com/p/micro-development-kit/
有什么问题,可以问我 都是服务器端的代码,能不能搞个好用点的客户端用的SOCKET库 mdk服务器引擎本身是一个server-client的一体化的通信引擎,没有client与server区别
你可以直接在client里面用
client server其实是一个相对概念,如果你的程序去连接别人,那么你是client别人是server
现在很多通信程序其实已经不再是一个单纯的client或server,
很多时候在connect别人的服务同时,可能自身也扮演一个服务的角色会接受其它连接
connect()去连接服务端,用listen()去接受某个端口上的连接
如果你client只需要连接服务器,不需要对外提供服务,可以不用listen()
io work线程数量也可以根据需要设置,如果不接受连接,建议设置成1
hopping 发表于 2012-12-1 12:22 static/image/common/back.gif
都是服务器端的代码,能不能搞个好用点的客户端用的SOCKET库
mdk服务器引擎本身是一个server-client的一体化的通信引擎,没有client与server区别
你可以直接在client里面用
client server其实是一个相对概念,如果你的程序去连接别人,那么你是client别人是server
现在很多通信程序其实已经不再是一个单纯的client或server,
很多时候在connect别人的服务同时,可能自身也扮演一个服务的角色会接受其它连接
connect()去连接服务端,用listen()去接受某个端口上的连接
如果你client只需要连接服务器,不需要对外提供服务,可以不用listen()
io work线程数量也可以根据需要设置,如果不接受连接,建议设置成1