当前位置: 首页 > news >正文

skynet.socket 完整通信流程

在这里插入图片描述

目录

  • 一、服务端(Server)通信流程
    • 1. 启动监听
    • 2. 接受客户端连接
    • 3. 接收客户端数据
    • 4. 主动关闭连接
  • 二、客户端(Client)通信流程
    • 1. 连接服务端
    • 2. 接收服务端数据
    • 3. 主动发送数据
    • 4. 断开连接
  • 三、完整交互时序图
  • 四、关键注意事项
    • 1. 非阻塞与协程
    • 2. 粘包/分包处理
    • 3. 错误处理
  • 五、性能优化建议
  • 六、示例代码整合
    • 服务端完整代码
    • 客户端完整代码

以下是基于 Skynet 框架的 Socket 通信流程详解,涵盖服务端与客户端的交互全过程,包括核心 API 使用、数据收发逻辑及示例代码:


一、服务端(Server)通信流程

1. 启动监听

  • API: socket.listen(host, port, backlog)
    • 功能: 绑定 IP 和端口,开启监听。
    • 参数:
      • host: 监听地址(如 "0.0.0.0" 表示所有网卡)。
      • port: 监听端口。
      • backlog: 等待连接队列的最大长度。
    • 返回值: 监听句柄 id,用于后续操作。
local skynet = require "skynet"
local socket = require "skynet.socket"

-- 启动服务端
skynet.start(function()
    local listen_id = socket.listen("0.0.0.0", 8888, 128)
    skynet.error("Server listening on port 8888")
    socket.start(listen_id, accept_handler)  -- 注册 accept_handler 处理新连接
end)

2. 接受客户端连接

  • API: socket.start(fd, accept_cb)
    • 功能: 将监听句柄 fd 托管给 Skynet,当有新连接时触发 accept_cb
  • 回调函数: accept_handler(fd, addr)
    • 参数:
      • fd: 新连接的客户端 Socket 句柄。
      • addr: 客户端地址(如 "127.0.0.1:12345")。
local function accept_handler(client_fd, addr)
    skynet.error("New client connected:", addr)
    socket.start(client_fd, receive_handler)  -- 注册 receive_handler 处理数据
end

3. 接收客户端数据

  • 回调函数: receive_handler(fd, data)
    • 触发条件: 当客户端发送数据时,Skynet 自动调用。
    • 参数:
      • fd: 客户端 Socket 句柄。
      • data: 收到的二进制数据(可能不完整)。
    • 注意: 需自行处理粘包/分包(参考前文的分包策略)。
local function receive_handler(client_fd, data)
    skynet.error("Received data from client", client_fd, ":", data)
    -- 示例:回显数据
    socket.write(client_fd, "Echo: " .. data)
end

4. 主动关闭连接

  • API: socket.close(fd)
    • 功能: 关闭指定 Socket 连接。
-- 在适当位置调用
socket.close(client_fd)

二、客户端(Client)通信流程

1. 连接服务端

  • API: socket.open(host, port)
    • 功能: 发起 TCP 连接到服务端。
    • 参数: 同 socket.listen
    • 返回值: 客户端 Socket 句柄 fd
local skynet = require "skynet"
local socket = require "skynet.socket"

skynet.start(function()
    local server_fd = socket.open("127.0.0.1", 8888)
    if server_fd then
        skynet.error("Connected to server")
        socket.start(server_fd, client_receive_handler)
        -- 发送数据示例
        socket.write(server_fd, "Hello Server!")
    else
        skynet.error("Connection failed")
    end
end)

2. 接收服务端数据

  • 回调函数: client_receive_handler(fd, data)
    • 逻辑与服务端 receive_handler 类似。
local function client_receive_handler(fd, data)
    skynet.error("Received from server:", data)
end

3. 主动发送数据

  • API: socket.write(fd, data)
    • 功能: 向指定 Socket 写入数据。
    • 注意: 数据需为字符串格式。
socket.write(server_fd, "Another message")

4. 断开连接

  • 与服务端关闭方式相同:
    socket.close(server_fd)
    

三、完整交互时序图

  Client                  Server
    |                        |
    |--- socket.open() ---->| 建立连接
    |                        |--触发 accept_handler
    |                        |
    |--- socket.write() ---->| 发送数据
    |                        |--触发 receive_handler
    |<--- socket.write() ----| 服务端响应
    |                        |
    |--- socket.close() ---->| 断开连接

四、关键注意事项

1. 非阻塞与协程

  • Skynet 的 Socket 操作是非阻塞的,所有收发操作通过回调异步处理。
  • 若需同步等待数据,可在协程中使用 socket.read
    local data = socket.read(fd, len)  -- 阻塞协程直到读取 len 字节
    

2. 粘包/分包处理

  • 必须设计应用层协议(如长度前缀、分隔符)确保数据完整性。
  • 示例协议解析(长度前缀):
    -- 服务端 receive_handler 中
    local buffer = {}  -- 按 fd 存储缓冲区
    function receive_handler(fd, data)
        if not buffer[fd] then buffer[fd] = "" end
        buffer[fd] = buffer[fd] .. data
        while true do
            if #buffer[fd] < 2 then break end  -- 假设长度字段为 2 字节
            local len = string.unpack(">I2", buffer[fd])
            if #buffer[fd] >= len + 2 then
                local packet = string.sub(buffer[fd], 3, len + 2)
                process_packet(fd, packet)
                buffer[fd] = string.sub(buffer[fd], len + 3)
            else
                break
            end
        end
    end
    

3. 错误处理

  • 监听或连接失败时返回 nil
  • 连接异常关闭时,回调函数的 data 参数为 nil
    function receive_handler(fd, data)
        if not data then
            skynet.error("Connection closed by peer:", fd)
            socket.close(fd)
            return
        end
        -- 正常处理数据
    end
    

五、性能优化建议

  1. 减少内存拷贝:

    • 使用 table 存储分片数据,最后用 table.concat 合并。
    local buffer_tbl = {}
    function receive_handler(fd, data)
        buffer_tbl[fd] = buffer_tbl[fd] or {}
        table.insert(buffer_tbl[fd], data)
        -- ...解析协议后清理 buffer_tbl
    end
    
  2. 连接池管理:

    • 对高频短连接场景,复用 Socket 句柄减少开销。
  3. 协议设计:

    • 优先使用二进制协议(如 string.pack/string.unpack)减少传输体积。

六、示例代码整合

服务端完整代码

local skynet = require "skynet"
local socket = require "skynet.socket"

local clients = {}  -- 存储客户端 fd

local function process_packet(fd, packet)
    skynet.error("Process packet from", fd, ":", packet)
    socket.write(fd, "ACK: " .. packet)
end

local function receive_handler(fd, data)
    if not data then
        skynet.error("Client disconnected:", fd)
        socket.close(fd)
        clients[fd] = nil
        return
    end
    -- 假设协议为长度前缀(2字节)
    if not clients[fd] then
        clients[fd] = { buffer = "" }
    end
    clients[fd].buffer = clients[fd].buffer .. data
    while true do
        if #clients[fd].buffer < 2 then break end
        local len = string.unpack(">I2", clients[fd].buffer)
        if #clients[fd].buffer >= len + 2 then
            local packet = string.sub(clients[fd].buffer, 3, len + 2)
            process_packet(fd, packet)
            clients[fd].buffer = string.sub(clients[fd].buffer, len + 3)
        else
            break
        end
    end
end

local function accept_handler(fd, addr)
    skynet.error("New client:", addr)
    clients[fd] = { buffer = "" }
    socket.start(fd, receive_handler)
end

skynet.start(function()
    local listen_fd = socket.listen("0.0.0.0", 8888, 128)
    socket.start(listen_fd, accept_handler)
    skynet.error("Server started on 8888")
end)

客户端完整代码

local skynet = require "skynet"
local socket = require "skynet.socket"

skynet.start(function()
    local fd = socket.open("127.0.0.1", 8888)
    if not fd then
        skynet.error("Connect failed")
        return
    end
    socket.start(fd, function(fd, data)
        if not data then
            skynet.error("Server closed")
            return
        end
        skynet.error("Received:", data)
    end)

    -- 发送带长度前缀的数据包
    local msg = "Hello World"
    local len = #msg
    local packed = string.pack(">I2", len) .. msg
    socket.write(fd, packed)
end)

通过以上流程,可清晰理解 Skynet 中 Socket 通信的完整生命周期。实际开发中需根据业务需求调整协议解析和错误处理逻辑。

相关文章:

  • Mysql8配置文件
  • 【贪心之摆动序列】
  • 三、Virtual Device Manager
  • CST1019.基于Spring Boot+Vue智能洗车管理系统
  • 从零手写RPC-version0
  • Android游戏逆向工程全面指南
  • MySQL慢查询全攻略:定位、分析与优化实战
  • Python 深度学习 第5章 机器学习的核心问题泛化及如何提高模型的泛化能力实例
  • 【MySQL】关于何时使用start slave和start slave user=‘’ password=‘’
  • 计算机的发展及应用
  • minio重大更新!RELEASE.2025-04-08T15-41-24Z发布:修复关键Bug,存储管理更高效!
  • 基于PyQt5的Jupyter Notebook转Python工具
  • 生物化学笔记:医学免疫学原理16 自身免疫和自身免疫性疾病
  • 电感特性参数、选型方法与厂商推荐
  • Spring Boot(二十二):RedisTemplate的List类型操作
  • DWS常用操作手册
  • Windows 10和Windows 11系统截图的9种方式【简便实用】
  • Mac M1管理多个Node.js版本
  • 深入解析区块链技术:原理、应用与未来展望
  • java(二):java的运算和流程控制
  • 世卫大会再次拒绝涉台提案,国台办:民进党当局再遭挫败理所当然
  • 消费维权周报丨上周涉汽车类投诉较多,涉加油“跳枪”等问题
  • 聚焦中华文明精神标识,多校专家学者跨学科对话交流
  • 3月中国减持189亿美元美债、持仓规模降至第三,英国升至第二
  • 既是工具又是食物,可食用机器人开启舌尖上的新科技
  • 俄方代表团抵达土耳其,俄乌直接谈判有望于当地时间上午重启