Skynet Lua API 参考手册

全面介绍 Skynet Lua API,包括核心模块、服务管理、消息通信、网络编程、定时器、集群等常用 API 的详细用法和示例

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:会话 ID
  • source:发送方地址
  • 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 ID
  • sz:要读取的字节数(可选)

返回值

  • 读取到的数据(字符串)或 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 ID
  • data:要写入的数据

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,包括:

  1. 服务生命周期:start、exit、register_exit_handler
  2. 服务管理:newservice、uniqueservice、globalservice
  3. 消息通信:send、call、ret、pack、unpack
  4. 协程和定时器:fork、sleep、timeout、now、time
  5. 网络编程:socket 模块的各种函数
  6. 集群通信:cluster 模块
  7. 数据共享:sharetable 模块

掌握这些 API 是开发 Skynet 应用的基础。在实际开发中,建议:

  • 多查阅官方文档
  • 参考开源项目代码
  • 编写测试用例验证 API 行为

参考资料

  1. Skynet API 文档:https://github.com/cloudwu/skynet/wiki/Api
  2. Skynet 源码:https://github.com/cloudwu/skynet
  3. Lua 5.4 参考手册:https://www.lua.org/manual/5.4/

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页