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