Skynet 提供了丰富的 Lua API,让开发者能够方便地使用框架的各种功能。本手册将详细介绍所有常用 API 的用法、参数和返回值。
核心模块
require “skynet”
这是 Skynet 的核心模块,提供了所有基础 API。
local skynet = require "skynet"
服务生命周期 API
skynet.start(func)
服务启动函数,在服务创建时调用。
local skynet = require "skynet"
skynet.start(function()
-- 服务初始化逻辑
skynet.error("服务启动")
-- 注册消息处理器
skynet.dispatch("lua", function(session, source, cmd, ...)
-- 处理消息
end)
end)
参数:
func:初始化函数,服务启动时执行
注意事项:
- 必须在服务脚本中调用一次
- 函数内应该注册消息处理器
- 不要在函数内执行长时间阻塞操作
skynet.exit()
退出当前服务。
local skynet = require "skynet"
skynet.start(function()
skynet.dispatch("lua", function(session, source, cmd, ...)
if cmd == "shutdown" then
skynet.exit() -- 退出服务
end
end)
end)
注意事项:
- 调用后服务立即退出
- 会触发退出回调(如果注册了)
- 未处理的消息会被丢弃
skynet.register_exit_handler(func)
注册服务退出时的回调函数。
local skynet = require "skynet"
local resources = {}
skynet.start(function()
resources.db = connect_database()
skynet.dispatch("lua", message_handler)
end)
skynet.register_exit_handler(function()
-- 清理资源
if resources.db then
resources.db:close()
end
skynet.error("服务已清理")
end)
参数:
func:退出回调函数
服务管理 API
skynet.self()
获取当前服务的地址。
local skynet = require "skynet"
skynet.start(function()
local addr = skynet.self()
skynet.error("当前服务地址:", string.format(":%08x", addr))
-- 输出类似::01000003
end)
返回值:
- 服务地址(number,32 位无符号整数)
skynet.newservice(name, …)
创建一个新的服务。
local skynet = require "skynet"
skynet.start(function()
-- 创建服务,不传参数
local service1 = skynet.newservice("my_service")
-- 创建服务,传递参数
local service2 = skynet.newservice("my_service", "arg1", 123)
skynet.error("新服务地址:", string.format(":%08x", service2))
end)
参数:
name:服务名称(字符串)...:传递给服务的参数
返回值:
- 新服务的地址(number)
注意事项:
- 每次调用都会创建新实例
- 服务名会在
luaservice路径中查找
skynet.uniqueservice(name, …)
创建或获取全局唯一服务。
local skynet = require "skynet"
skynet.start(function()
-- 第一次调用:创建服务
local service1 = skynet.uniqueservice("singleton")
-- 第二次调用:返回同一个服务
local service2 = skynet.uniqueservice("singleton")
assert(service1 == service2) -- 相同的服务地址
end)
参数:
name:服务名称...:传递给服务的参数(仅首次创建时使用)
返回值:
- 服务地址(number)
注意事项:
- 全局唯一,整个 Skynet 节点只有一个实例
- 多次调用返回相同地址
- 参数只在首次创建时有效
skynet.globalservice(name, …)
创建或获取集群全局唯一服务。
local skynet = require "skynet"
skynet.start(function()
-- 集群中全局唯一
local service = skynet.globalservice("global_service")
end)
参数:同 uniqueservice
返回值:服务地址
注意事项:
- 在整个集群中唯一(跨多个 Skynet 节点)
- 需要配置集群
skynet.register(name)
为当前服务注册别名。
local skynet = require "skynet"
skynet.start(function()
-- 注册别名
skynet.register(".my_service")
-- 其他服务可以通过别名访问
local addr = skynet.localname(".my_service")
skynet.call(addr, "lua", "hello")
end)
参数:
name:别名(通常以.开头)
skynet.localname(name)
通过别名获取服务地址。
local skynet = require "skynet"
skynet.start(function()
local addr = skynet.localname(".my_service")
if addr then
skynet.send(addr, "lua", "message")
else
skynet.error("服务不存在")
end
end)
参数:
name:服务别名
返回值:
- 服务地址(number)或 nil(不存在时)
消息通信 API
skynet.send(addr, type, …)
发送消息,不等待响应。
local skynet = require "skynet"
skynet.start(function()
local target = skynet.newservice("target")
-- 发送 Lua 消息
skynet.send(target, "lua", "command", arg1, arg2)
-- 发送文本消息
skynet.send(target, "text", "hello world")
-- 发送到别名
skynet.send(".my_service", "lua", "notify")
end)
参数:
addr:目标服务地址或别名type:消息类型(“lua”、“text”、“client” 等)...:消息内容
返回值:
- 无
skynet.call(addr, type, …)
发送消息并等待响应。
local skynet = require "skynet"
skynet.start(function()
local target = skynet.newservice("target")
-- 调用并等待响应
local result = skynet.call(target, "lua", "query", "key")
skynet.error("结果:", result)
-- 处理错误
local ok, data = pcall(skynet.call, target, "lua", "risky_command")
if not ok then
skynet.error("调用失败:", data)
end
end)
参数:
addr:目标服务地址或别名type:消息类型...:消息内容
返回值:
- 响应结果(可以是多个值)
注意事项:
- 会阻塞当前协程直到收到响应
- 如果目标服务崩溃,会抛出异常
skynet.ret(msg)
返回响应消息。
local skynet = require "skynet"
skynet.start(function()
skynet.dispatch("lua", function(session, source, cmd, ...)
if cmd == "query" then
local result = do_query(...)
if session ~= 0 then
skynet.ret(skynet.pack(result))
end
end
end)
end)
参数:
msg:打包后的消息数据
skynet.retpack(…)
打包并返回响应(skynet.ret + skynet.pack 的快捷方式)。
local skynet = require "skynet"
skynet.start(function()
skynet.dispatch("lua", function(session, source, cmd, ...)
if cmd == "query" then
local result = do_query(...)
if session ~= 0 then
skynet.retpack(result) -- 简化写法
end
end
end)
end)
参数:
...:要返回的数据
skynet.pack(…)
打包数据为二进制格式。
local skynet = require "skynet"
local data = {
name = "张三",
age = 25,
items = {1, 2, 3}
}
local packed = skynet.pack(data)
-- packed 是二进制字符串,可以通过消息传递
参数:
...:要打包的数据(支持多个值)
返回值:
- 打包后的二进制数据
支持的类型:
- nil、boolean、number、string
- table(支持嵌套,但不支持循环引用)
- userdata(C 指针)
skynet.unpack(data)
解包二进制数据。
local skynet = require "skynet"
local packed = skynet.pack("hello", 123, {a = 1})
local str, num, tbl = skynet.unpack(packed)
skynet.error(str, num, tbl.a) -- hello 123 1
参数:
data:打包后的二进制数据
返回值:
- 解包后的数据(可以是多个值)
消息分发 API
skynet.dispatch(type, func)
注册消息处理函数。
local skynet = require "skynet"
skynet.start(function()
-- 处理 Lua 消息
skynet.dispatch("lua", function(session, source, cmd, ...)
skynet.error("收到 Lua 消息:", cmd, ...)
if session ~= 0 then
skynet.retpack("response")
end
end)
-- 处理 Socket 消息
skynet.dispatch("socket", function(session, source, cmd, ...)
skynet.error("收到 Socket 消息:", cmd)
end)
end)
参数:
type:消息类型(“lua”、“socket”、“text” 等)func:处理函数session:会话 ID(0 表示 send,非 0 表示 call)source:发送方服务地址...:消息内容
回调函数参数:
session:会话 IDsource:发送方地址cmd:命令名(对于 lua 消息)...:其他参数
skynet.register_protocol(config)
注册自定义消息协议。
local skynet = require "skynet"
local cjson = require "cjson"
-- 注册 JSON 协议
skynet.register_protocol({
name = "json",
id = skynet.PTYPE_TEXT,
pack = function(data)
return cjson.encode(data)
end,
unpack = function(data)
return cjson.decode(data)
end,
})
skynet.start(function()
-- 发送 JSON 消息
skynet.send(target, "json", {type = "chat", msg = "hello"})
-- 接收 JSON 消息
skynet.dispatch("json", function(session, source, data)
skynet.error("收到 JSON:", data.type, data.msg)
end)
end)
参数:
config:协议配置表name:协议名称id:协议 ID(数字)pack:打包函数unpack:解包函数
协程和定时器 API
skynet.fork(func, …)
创建新协程执行函数。
local skynet = require "skynet"
skynet.start(function()
-- 创建后台任务
skynet.fork(function()
while true do
skynet.sleep(100) -- 每秒执行一次
do_background_work()
end
end)
-- 创建带参数的协程
skynet.fork(function(id)
skynet.error("处理任务:", id)
end, 123)
-- 主协程继续
skynet.dispatch("lua", message_handler)
end)
参数:
func:要执行的函数...:传递给函数的参数
返回值:
- 协程对象
注意事项:
- 协程独立运行,不影响主消息处理
- 适合执行后台任务或长时间运行的操作
skynet.sleep(time)
休眠指定时间(单位:0.01 秒)。
local skynet = require "skynet"
skynet.start(function()
skynet.fork(function()
skynet.error("开始休眠")
skynet.sleep(100) -- 休眠 1 秒
skynet.error("休眠结束")
end)
end)
参数:
time:休眠时间(单位:0.01 秒,100 = 1 秒)
注意事项:
- 会挂起当前协程
- 不会阻塞整个服务,其他协程可以继续运行
skynet.timeout(time, func)
设置定时器。
local skynet = require "skynet"
skynet.start(function()
-- 1 秒后执行
skynet.timeout(100, function()
skynet.error("定时器触发")
end)
-- 周期性执行
local function tick()
skynet.error("tick")
skynet.timeout(100, tick) -- 每秒执行一次
end
skynet.timeout(100, tick)
end)
参数:
time:延迟时间(单位:0.01 秒)func:回调函数
注意事项:
- 定时器是一次性的,需要重新设置才能周期性执行
- 回调函数在独立的协程中执行
skynet.now()
获取当前时间(单位:0.01 秒)。
local skynet = require "skynet"
skynet.start(function()
local start = skynet.now()
-- 执行一些操作
do_something()
local elapsed = skynet.now() - start
skynet.error("耗时:", elapsed / 100, "秒")
end)
返回值:
- 从 Skynet 启动到现在的时间(单位:0.01 秒)
skynet.time()
获取当前系统时间(Unix 时间戳)。
local skynet = require "skynet"
skynet.start(function()
local timestamp = skynet.time()
skynet.error("当前时间:", os.date("%Y-%m-%d %H:%M:%S", timestamp))
end)
返回值:
- Unix 时间戳(秒)
日志 API
skynet.error(…)
输出错误日志。
local skynet = require "skynet"
skynet.start(function()
skynet.error("这是一条错误日志")
skynet.error("多个参数:", "hello", 123, {a = 1})
end)
参数:
...:日志内容(支持多个参数)
输出格式:
[:01000003] 这是一条错误日志
[:01000003] 多个参数: hello 123 table: 0x7f8b8c0a3e80
skynet.trace()
输出追踪日志(仅在调试模式下有效)。
local skynet = require "skynet"
skynet.start(function()
skynet.trace()
skynet.error("这条日志会包含调用栈信息")
end)
网络编程 API
skynet.socket 模块
local skynet = require "skynet"
local socket = require "skynet.socket"
socket.listen(address, port)
监听 TCP 端口。
local skynet = require "skynet"
local socket = require "skynet.socket"
skynet.start(function()
-- 监听端口
local id = socket.listen("0.0.0.0", 8888)
skynet.error("监听端口 8888")
-- 接受连接
socket.start(id, function(conn_id, addr)
skynet.error("新连接:", conn_id, addr)
-- 为新连接创建处理协程
skynet.fork(function()
socket.start(conn_id)
while true do
local data = socket.readline(conn_id, "\n")
if not data then
break
end
skynet.error("收到:", data)
socket.write(conn_id, "Echo: " .. data .. "\n")
end
socket.close(conn_id)
end)
end)
end)
参数:
address:监听地址(如 “0.0.0.0”)port:端口号
返回值:
- 监听 socket ID
socket.open(addr, port)
连接到远程服务器。
local skynet = require "skynet"
local socket = require "skynet.socket"
skynet.start(function()
skynet.fork(function()
-- 连接到服务器
local id = socket.open("127.0.0.1", 8888)
skynet.error("已连接")
-- 发送数据
socket.write(id, "Hello\n")
-- 接收数据
local data = socket.readline(id, "\n")
skynet.error("收到:", data)
socket.close(id)
end)
end)
参数:
addr:服务器地址port:端口号
返回值:
- socket ID
socket.read(id, sz)
读取数据。
local skynet = require "skynet"
local socket = require "skynet.socket"
skynet.start(function()
skynet.fork(function()
local id = socket.open("127.0.0.1", 8888)
-- 读取指定字节数
local data = socket.read(id, 1024)
-- 读取一行
local line = socket.readline(id, "\n")
-- 读取所有数据(直到连接关闭)
local all = socket.readall(id)
socket.close(id)
end)
end)
参数:
id:socket IDsz:要读取的字节数(可选)
返回值:
- 读取到的数据(字符串)或 nil(连接关闭)
socket.write(id, data)
写入数据。
local skynet = require "skynet"
local socket = require "skynet.socket"
skynet.start(function()
skynet.fork(function()
local id = socket.open("127.0.0.1", 8888)
-- 写入数据
socket.write(id, "Hello World\n")
-- 写入二进制数据
local binary = string.char(0x01, 0x02, 0x03)
socket.write(id, binary)
socket.close(id)
end)
end)
参数:
id:socket IDdata:要写入的数据
socket.close(id)
关闭 socket。
local skynet = require "skynet"
local socket = require "skynet.socket"
skynet.start(function()
skynet.fork(function()
local id = socket.open("127.0.0.1", 8888)
-- 使用完毕后关闭
socket.close(id)
end)
end)
集群 API
skynet.cluster 模块
local skynet = require "skynet"
local cluster = require "skynet.cluster"
cluster.reload(config)
重新加载集群配置。
local skynet = require "skynet"
local cluster = require "skynet.cluster"
skynet.start(function()
cluster.reload({
node1 = "127.0.0.1:7001",
node2 = "127.0.0.1:7002",
node3 = "127.0.0.1:7003",
})
end)
参数:
config:集群配置表(节点名 → 地址)
cluster.open(port)
开启集群监听。
local skynet = require "skynet"
local cluster = require "skynet.cluster"
skynet.start(function()
-- 开启集群端口
cluster.open(7001)
skynet.error("集群端口已开启: 7001")
end)
参数:
port:监听端口
cluster.call(node, addr, …)
调用远程节点的服务。
local skynet = require "skynet"
local cluster = require "skynet.cluster"
skynet.start(function()
-- 调用 node1 节点的服务
local result = cluster.call("node1", ".my_service", "lua", "query", "key")
skynet.error("远程调用结果:", result)
end)
参数:
node:节点名称addr:服务地址或别名...:消息内容
返回值:
- 远程服务的响应
cluster.send(node, addr, …)
发送消息到远程节点(不等待响应)。
local skynet = require "skynet"
local cluster = require "skynet.cluster"
skynet.start(function()
-- 发送消息到远程节点
cluster.send("node2", ".logger", "lua", "log", "hello from node1")
end)
参数:同 cluster.call
数据共享 API
skynet.sharetable 模块
local skynet = require "skynet"
local sharetable = require "skynet.sharetable"
sharetable.new(name, tbl)
创建共享表。
local skynet = require "skynet"
local sharetable = require "skynet.sharetable"
skynet.start(function()
-- 创建共享配置表
sharetable.new("config", {
max_players = 100,
server_name = "My Server"
})
end)
参数:
name:共享表名称tbl:初始数据
sharetable.query(name)
查询共享表。
local skynet = require "skynet"
local sharetable = require "skynet.sharetable"
skynet.start(function()
-- 查询共享表
local config = sharetable.query("config")
skynet.error("最大玩家数:", config.max_players)
end)
参数:
name:共享表名称
返回值:
- 共享表的只读副本
sharetable.update(name, tbl)
更新共享表。
local skynet = require "skynet"
local sharetable = require "skynet.sharetable"
skynet.start(function()
-- 更新共享表
sharetable.update("config", {
max_players = 200, -- 修改值
server_name = "My Server"
})
end)
参数:
name:共享表名称tbl:新数据
实战示例:完整的 API 使用
游戏房间服务
-- service/room.lua
local skynet = require "skynet"
local socket = require "skynet.socket"
local players = {}
local room_id = ...
local CMD = {}
function CMD.join(player_id, conn_id)
if players[player_id] then
return false, "已经在房间中"
end
players[player_id] = {
conn_id = conn_id,
join_time = skynet.time()
}
skynet.error(string.format("玩家 %s 加入房间 %s", player_id, room_id))
-- 通知其他玩家
for pid, pdata in pairs(players) do
if pid ~= player_id then
socket.write(pdata.conn_id,
string.format("PLAYER_JOIN:%s\n", player_id))
end
end
return true
end
function CMD.leave(player_id)
if not players[player_id] then
return false, "不在房间中"
end
players[player_id] = nil
skynet.error(string.format("玩家 %s 离开房间 %s", player_id, room_id))
-- 通知其他玩家
for pid, pdata in pairs(players) do
socket.write(pdata.conn_id,
string.format("PLAYER_LEAVE:%s\n", player_id))
end
return true
end
function CMD.broadcast(message)
for pid, pdata in pairs(players) do
socket.write(pdata.conn_id, message .. "\n")
end
end
function CMD.get_players()
local list = {}
for pid in pairs(players) do
list[#list + 1] = pid
end
return list
end
skynet.start(function()
skynet.error(string.format("房间 %s 启动", room_id))
skynet.dispatch("lua", function(session, source, cmd, ...)
local f = assert(CMD[cmd], "Unknown command: " .. cmd)
if session ~= 0 then
skynet.retpack(f(...))
else
f(...)
end
end)
-- 定时检查超时玩家
skynet.fork(function()
while true do
skynet.sleep(6000) -- 每分钟检查一次
local now = skynet.time()
for pid, pdata in pairs(players) do
if now - pdata.join_time > 3600 then -- 超过 1 小时
CMD.leave(pid)
end
end
end
end)
end)
总结
本手册详细介绍了 Skynet 的 Lua API,包括:
- 服务生命周期:start、exit、register_exit_handler
- 服务管理:newservice、uniqueservice、globalservice
- 消息通信:send、call、ret、pack、unpack
- 协程和定时器:fork、sleep、timeout、now、time
- 网络编程:socket 模块的各种函数
- 集群通信:cluster 模块
- 数据共享:sharetable 模块
掌握这些 API 是开发 Skynet 应用的基础。在实际开发中,建议:
- 多查阅官方文档
- 参考开源项目代码
- 编写测试用例验证 API 行为
参考资料
- Skynet API 文档:https://github.com/cloudwu/skynet/wiki/Api
- Skynet 源码:https://github.com/cloudwu/skynet
- Lua 5.4 参考手册:https://www.lua.org/manual/5.4/
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。