Skynet Lua API 参考手册

Skynet 提供了丰富的 Lua API,让开发者能够方便地使用框架的各种功能。本手册将详细介绍所有常用 API 的用法、参数和返回值。核心模块 require "skynet" 这是 Skynet 的核心模块,提供了所有基础 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/

继续阅读

探索更多技术文章

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

全部文章 返回首页