昨天凌晨4点还在写Skynet 服务试玩,结果发现自己写的服务出现内存泄漏了。
今天早上起床吃早餐的时候一直在想:
"如果每个Lua服务处理完客户端关闭的网络连接后,服务是否会自动退出呢?"
答案:不会!
现在,我们来看昨天写的Lua网络连接处理服务:
首先是main.lua:
1local skynet = require "skynet" 2local socket = require "socket" 3 4 5 6skynet.start(function() 7 8 local id = socket.listen("192.168.2.5",80) 9 10 socket.start(id , function(fd,addr) 11 12 local cnn = skynet.newservice "network" 13 skynet.send(cnn,"lua","SOCKET",fd) 14 socket.abandon(fd) 15 16 end) 17 --skynet.exit() 18end)
这里没什么问题,毕竟如果你skynet在start函数内退出,你监听的套接字也将被清空。
然后是network.lua:
1local skynet = require "skynet" 2local socket = require "socket" 3 4skynet.init(function() 5 print("init was success...") 6end) 7 8local CMD = { } 9 10 11function CMD.SOCKET(fd) 12 13 socket.start(fd) 14 socket.write(fd,"welcome!\n\r>>") 15 16 while fd do 17 18 local buf = socket.readline(fd) 19 20 if buf and buf ~= "exit()" then 21 22 socket.write(fd,"you say: " .. buf .. "\r\n>>") 23 24 else 25 skynet.error("Client Closed this Connection...") 26 socket.close(fd) 27 break 28 end 29 end 30 31end 32 33skynet.start(function() 34 skynet.dispatch("lua",function(_,_,cmd,fd) 35 36 local f = assert(CMD[cmd]) 37 if f then 38 f(fd) 39 end 40 end) 41end)
首先while语句开始,读取client 内容然后进入判断。
然后socket阻塞连接并等待客户端数据。
最后客户端关闭连接进入socket.close阶段,并且退出While循环。
粗略一下没什么问题!但是细心的朋友可以看出,前面main文件在start方法内没调用skynet.exit就会一直存在。那我们怎么知道一个服务知否存在呢? 这就要靠debug_console了,我们简单修改一下main.lua文件,启动debug_console服务。
修改后的main.lua文件如下:
1local skynet = require "skynet" 2local socket = require "socket" 3 4 5skynet.start(function() 6 skynet.newservice("debug_console",8000) -- 开启debug_console服务 7 local id = socket.listen("192.168.2.5",80) 8 socket.start(id,function(fd,ipaddr) 9 print(fd,ipaddr) 10 local cnn = skynet.newservice "network" 11 print(cnn) 12 skynet.send(cnn,"lua","SOCKET",fd) 13 socket.abandon(fd) 14 end) 15 16end)
我在开始时,先启动debug_console服务并监听8000端口;然后我们使用telnet连接上去。

这个"7 connected" 表示我们的console已经连接上了。

然而我们的客户端还未进行连接,所以我们先list一下后发现network服务是没有启动;

这时候,可以在cmd命令行内输入 telnet 192.168.2.5 80来发起一个客户端连接。

好的,服务端已经进行了正确的回应。这时候我们在console 内再次进行list;

network的lua服务已经启动并且接手管理socket连接相关处理,而我们的服务内只是简单完成了请求、回应相同数据的逻辑。
这时候我们将client断开后再list会发现:network服务没有按照我预先想的那样自己退出。debug单步调试后也发现连接被close掉了。而此外你gc也仅仅只是进行lua代码的垃圾回收,而无法退出一个服务、

至此,我们只能使用kill命令强行杀掉这个服务了。

我们的lua服务必须在每个连接close后主动调用skynet.exit,否则一个隐式的内存泄漏将会产生。
如果我们在一个万人在线的游戏服务器不小心犯了这个错误,每个用户只要重复登录5次,服务器的内存将会瞬间被吃光。