第一个程序PingPong
功能需求
如图所示,开启两个ping类型的服务ping1和ping2,ping1给ping2发消息,ping2收到回应ping1,ping1收到再回应ping2,不断循环。
服务模块
Skynet提供了开启服务发送消息的API,必先掌握它们。表2-5列出了Skynet中8个最重要的API,PingPong程序会用到它们。更多API可以参见 Skynet API
Lua API | 说明 |
newservice(name,...) |
启动一个名(类型)为name的新服务,并返回新服务的地址,地址格式为:0100000f、01000009即表示服务地址,同节点内的服务会有唯一地址,例如 local ping1 = skynet.newservice("ping") 表示开启一个ping类型的服务,把地址存放到ping1中 |
start(func) | 用func函数初始化服务。编写服务时,都会写一名skynet.start,并在func写一些初始化代码 |
dispatch(type,func) |
为type类型的消息设定处理函数fun。Skynet支持多种消息类型,由于Lua服务间的消息类型是“lua”,因此这里暂时将它固定为“lua”。func是指收到消息的处理函数,当一个服务收到新消息时,Skynet就会开启新协程,并调用它。func的形式为 function(session, source, cmd,...) ...... end 参数 session 代表消息的唯一id,source代表消息来源,指发送消息的服务地址,cmd代表消息号,"..."是一个可变参数,内容由发送方的skynet.send 或 skynet.call 指定 编写服务,一般会用如下的固定形式。表示以匿名函数的方式编写 skynet.start的参数func,并在func中调用dispatch skynet.start(function() skynet.dispatch("lua",function(参数略) ...... end) end) |
send(addr,type,cmd,...) |
向地址为addr的服务发送一条type类型的消息,消息名为cmd。发送方用skynet.send发送消息,接收文用skynet.dispatch接收消息,它们的参数相互对应。若用于服务间通信,类型一般固定为"lua" 例如,使用如下语句向服务 ping1 发送消息 skynet.send(ping1, "lua", "ping", 1, 2) 在ping1的dispatch回调中,参数的值如下 function(session, source, cmd, p1, p2, p3) -- cmd = "ping" -- p1 = 1 -- p2 = 2 -- p3 = nil end |
call(addr,type,cmd,...) | 向地址为addr的服务发送一条type类型的消息,并等待对方的回应。skynet.call是个阻塞方法 |
exit() | 结束当前服务 |
self() | 返回当前服务的地址 |
error(msg) | 向log服务发送一条消息,即打印日志 |
skynet.call的示意图
代码实现
PingPong程序必须包含主服务和ping服务。
配置文件(examples/Pconfig)
include "./config.path"
-- preload = "./preload.lua" -- run preload.lua before every lua service run
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "Pmain" -- main script
bootstrap = "snlua bootstrap" -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"
入口代码(examples/Pmain.lua)
local skynet = require "skynet"
skynet.start(function()
skynet.error("[Pmain] start")
local ping1 = skynet.newservice("ping")
local ping2 = skynet.newservice("ping")
skynet.send(ping1, "lua", "start", ping2)
skynet.exit()
end)
逻辑代码(examples/ping.lua)
local skynet = require "skynet"
local CMD = {}
function CMD.start(source, target)
skynet.error("[ping] start..")
skynet.send(target, "lua", "ping", 1)
end
function CMD.ping(source, count)
local id = skynet.self()
skynet.error("["..id.. "] recv ping count="..count)
skynet.sleep(100)
skynet.send(source, "lua", "ping", count+1)
end
skynet.start(function()
skynet.dispatch("lua", function(session, source, cmd, ...)
skynet.error("[ping] dispatch cmd="..cmd)
if not CMD[cmd] then
skynet.error("[ping] dispatch CMD[cmd] nil")
return
end
local f = assert(CMD[cmd])
f(source, ...)
end)
end)
运行代码
root@server-VirtualBox:/home/skynet# ./skynet examples/Pconfig
[:01000002] LAUNCH snlua bootstrap
[:01000003] LAUNCH snlua launcher
[:01000004] LAUNCH snlua cmaster
[:01000004] master listen socket 0.0.0.0:2013
[:01000005] LAUNCH snlua cslave
[:01000005] slave connect to master 127.0.0.1:2013
[:01000004] connect from 127.0.0.1:49210 4
[:01000006] LAUNCH harbor 1 16777221
[:01000004] Harbor 1 (fd=4) report 127.0.0.1:2526
[:01000005] Waiting for 0 harbors
[:01000005] Shakehand ready
[:01000007] LAUNCH snlua datacenterd
[:01000008] LAUNCH snlua service_mgr
[:01000009] LAUNCH snlua Pmain
[:01000009] [Pmain] start
[:0100000a] LAUNCH snlua ping
[:0100000b] LAUNCH snlua ping
[:01000009] KILL self
[:0100000a] [ping] dispatch cmd=start
[:0100000a] [ping] start..
[:01000002] KILL self
[:0100000b] [ping] dispatch cmd=ping
[:0100000b] [16777227] recv ping count=1
[:0100000a] [ping] dispatch cmd=ping
[:0100000a] [16777226] recv ping count=2
[:0100000b] [ping] dispatch cmd=ping
[:0100000b] [16777227] recv ping count=3
[:0100000a] [ping] dispatch cmd=ping
[:0100000a] [16777226] recv ping count=4
[:0100000b] [ping] dispatch cmd=ping
[:0100000b] [16777227] recv ping count=5
[:0100000a] [ping] dispatch cmd=ping
[:0100000a] [16777226] recv ping count=6