NetHost指针访问安全专集(基于mdk1.10)

NetHost是服务器引擎的2大接口类之一,并且是由引擎维护其对象的建立与释放, 安全

用户使用不当可能形成访问野指针或者内存泄露。 服务器

为了让使用mdk的朋友,安全的使用NetHost指针,本文写提出一些安全的使用模型。

首先说明一下,若是担忧NetHost使用不当,能够彻底放弃NetHost,引擎的另一大接口类NetServer已经提供了SendMsg()与CloseConnect()方法,用于安全的操做主机的链接,只要传递HostID进去(能够经过NetHost->ID()方法获得)便可,ID只是一个int值,不存在内存泄露与野指针问题。

接收数据,在OnMsg中使用参数的NetHost指针是绝对安全的,引擎已经作好控制。


开始NetHost的安全访问 并发

NetHost表示一个链接上来的主机,该类主要就是2个做用send发消息close断开链接,另外提供方便的Group操做 spa


为了不方法野指针和内存泄露,主要要了解NetHost对象的生存周期
NetHost对象在链接创建时被建立
链接断开(不论是server断开,仍是client断开)后被移动到关闭列表(代码中就是m_closedConnects里面),
后台有一个释放线程,会循环定时去检查这个列表中的指针,若是对象的访问计数为0,则释放掉对象

对象什么时是绝对安全的(不会被释放)
1.被做为参数传递给OnConnect OnMsg OnClose时,参数中的NetHost指针是绝对不会被引擎释放的,
由于引擎在调用这3个方法前已经将对象的方法计数+1,在退出后会将访问计数-1,也就是说这3个方法一退出,对象就有可能被释放。
2.OnClose被调用前,NetHost对象绝对不会被引擎释放,也就是说OnConnect OnMsg退出后,指针依旧是安全的,
但这不表示,你在OnConnect中将指针复制之后,下次在OnMsg中使用就是安全的,由于OnConnect OnMsg OnClose是并发的

为了避免让底层释放对象,用户可使用惟一的方法——使用Hold()来将访问计数+1,
用完之后记得使用Free()来将访问计数-1,好让引擎最终有机会释放对象,不然引用计数没法归0,引擎永远不会释放对象,这就是内存泄露

下面举例说明一些须要Hold Free的状况
做过服务器开发的应该很容易想到,一个链接创建了在OnConnect中就要保存表示这个链接的对象,
未来在OnMsg或其它业务中,用这个对象来通知其该玩家,没错服务器对于链接的使用就是这样,没有其它花样。

那么咱们创建一个map<int,NetHost*> list用于保存全部链接
好比有3个玩家abc链接进来
onconnect(pPlayer)
步骤1.lock list
步骤2.pPlayer->Hold()访问计数+1
步骤3.将指针保存到list  //abc到了list里面,而且访问计数=1,即便链接断开,在用户调用Free()以前,引擎不会释放他们
步骤4.unlock

onclose(pPlayer)
步骤1.lock list
步骤2.将指针从list删除
步骤3.unlock
步骤4.pPlayer->Free()不在使用了,访问计数-1

好比玩家a攻击了玩家b
onmsg(pPlayer)
步骤1.lock list
步骤2.在list中找到玩家b的NetHost指针赋值给pPlayerB
步骤3.unlock
步骤4.pPlayerB->Send()通知玩家B,受到玩家A的攻击

如今开始作状况分析
if步骤4以前,玩家B退出游戏,而且发生线程切换,进入onclose()
结果就是pPlayerB指向的对象访问计数归0,引擎得到了该对象的释放权利(虽然不必定会马上释放,由于是定时去检查要不要释放)。
但这就已经不安全了,服务器7*24小时长期运行,并且几千用户链接断开,要碰到正好引擎把指针给释放了,概率很是高。
结果就是线程切换回onmsg执行步骤4时,访问一个野指针,服务器崩溃。

就是说在步骤4.以前要pPlayer->Hold()1下,让访问计数变为2,
万一这时B退出,访问计数仍是1,引擎不会释放对象,
步骤4以后,再pPlayer->Free()下,若是B退出了就是0,没退出就1,正常

这样就没问题了?
不对pPlayer->Hold()和pPlayerB->Send()没有区别都是在unlock以外使用pPlayerB这个指针,一样会被线程切换打断,致使访问野指针
因此pPlayer->Hold()必须放在lock unlock内。这样就没问题了

总结一下就是,指针进入列表,从列表取出来使用,都要在lock unlock以内完成hold,由于hold在lock以内,能够阻止释放线程先行free,由于free要在从列表删除以后,也就是lock以后(线程会被挂起),而free能够在unlock前(互斥区内),也能够在unlock以后(互斥区外)进行,是安全的。

这样的话,若是OnMsg步骤1lock以前,玩家B退出游戏,发生线程切换,OnClose中完成free,则线程再次切换回OnMsg时候list中已经取不到玩家B的对象了,表示玩家已退出,一切正常。


最后OnMsg中不Hold Free可不能够?
答:能够,将步骤4放到lock以内就好了

这样最简单,
onconnect()时lock hold,进列表
onclose()时lock从列表删除unlock free
要用的时候lock,要么对象已经被从列表中删除,要么阻止onclose线程,用完unlock,没必要hold free
相关文章
相关标签/搜索