Skip to content

Conversation

@mostevercxz
Copy link

@mostevercxz mostevercxz commented Jan 18, 2017

复现方法:
在地图内(1,1)-(800,800)范围内配置20000个实体,人物 aoiRadius设置为200,aoiRadius 每秒增加 10,人物在游戏中移动,过段时间便会掉线

问题具体代码分析:

  1. 服务器发送 onUpdatePropertys(消息体:78字节) 和 onEntityEnterWorld(消息体:6字节) 给客户端
  2. 客户端 PacketReceiver 的缓冲区里收到了消息 onUpdatePropertys 的全部和 onEntityEnterWorld 的部分长度(假设收到的 msglen=3)
  3. onUpdatePropertys 收到后,根据 eid 找不到实体,从内存池中拿一个 MemoryStream (记为 propMemoryStream )将收到的消息内容拷贝至该 propMemoryStream 中(此时 wpos=78),并插入 _bufferedCreateEntityMessage
  4. 处理到 onEntityEnterWorld 时候,发现消息没收全,于是 break 掉,跳出函数 PacketReceiver::process(), 走到 KBEngineApp::sendTick() 函数
  5. sendTick() 中 调用 Bundle::newMessage() 时 Bundle 里的 MemoryStream 刚好是 propMemoryStream,于是从 wpos = 78 的地方开始写消息ID,长度,内容;最后 packetSender 发送的时候从 wpos=0 开始发送数据,导致服务器收到的 msgID 是乱的,将 pChannel->condemn(), 客户端掉线

为何 Bundle::newMessage() 里的 MemoryStream 是被 onUpdatePropertys 用过的呢?

  1. Bundle::send() 时候先调用 fini(true), fini 函数从池子里取出来一个新的 MemoryStream 赋值给 stream
  2. 再遍历 streamList, 但此时又将 stream 赋值为 streamList[i](此步骤是错误的根源)
  3. 遍历 streamList,将旧的 MemoryStream 还给内存池,此时 onUpdatePropertys 从内存池刚好取到 Bundle 还的 MemoryStream,但 Bundle 还在继续使用该 MemoryStream.

复现方法:
在地图内(1,1)-(800,800)范围内配置20000个实体,人物 aoiRadius设置为200,aoiRadius  每秒增加 10,人物在游戏中移动,过段时间便会掉线

问题具体代码分析:
1. 服务器发送 onUpdatePropertys(消息体:78字节) 和 onEntityEnterWorld(消息体:6字节) 给客户端
2. 客户端 PacketReceiver 的缓冲区里收到了消息 onUpdatePropertys 的全部和 onEntityEnterWorld 的部分长度(假设收到的 msglen=3)
3. onUpdatePropertys 收到后,根据 eid 找不到实体,从内存池中拿一个 MemoryStream (记为 propMemoryStream )将收到的消息内容拷贝至该 propMemoryStream 中(此时 wpos=78),并插入 _bufferedCreateEntityMessage
4. 处理到 onEntityEnterWorld 时候,发现消息没收全,于是 break 掉,跳出函数 PacketReceiver::process(), 走到 KBEngineApp::sendTick() 函数
5. sendTick() 中 调用 Bundle::newMessage() 时 Bundle 里的 MemoryStream 刚好是 propMemoryStream,于是从 wpos = 78 的地方开始写消息ID,长度,内容;最后 packetSender 发送的时候从 wpos=0 开始发送数据,导致服务器收到的 msgID 是乱的,将 pChannel->condemn(), 客户端掉线

为何 Bundle::newMessage() 里的 MemoryStream 是被 onUpdatePropertys 用过的呢?
1. Bundle::send() 时候先调用 fini(true), fini 函数从池子里取出来一个新的 MemoryStream
2. 再遍历 streamList, 但此时又将 stream 赋值为 streamList[i](此步骤是错误的根源)
3. 遍历 streamList,将旧的 MemoryStream 还给内存池,此时 onUpdatePropertys 从内存池刚好取到 Bundle 还的 MemoryStream,但 Bundle 还在继续使用该 MemoryStream.
@mostevercxz
Copy link
Author

mostevercxz commented Jan 18, 2017

掉线的问题查了1天半
在所有更改 MemoryStream 的 wpos 的地方都加了日志
在 PacketSender::send 里面判断消息id找不到,加条错误日志
分析错误日志出现的上方所有对 MemoryStream 的操作,最终找到问题。 :-)

@kbengine
Copy link
Owner

碰巧昨天刚修复个类似问题:
#10
17736a5

非常感谢, streamList遍历和stream.clear();处在多线程确实有一些隐患。

@kbengine kbengine merged commit dc29a64 into kbengine:master Jan 18, 2017
@mostevercxz
Copy link
Author

@kbengine 我刚才没仔细看你修复 #10 的代码,以为我代码提交了也能修复 #10 ..我还特意把 clear() 方法从 Bundle::reclaimObject() 中删除了,还让你重新commit一次...
非常感谢 kbe 引擎还有维护者们,一起完善代码,让kbe变得更强大哈 :-)

@kbengine
Copy link
Owner

;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants