skynet gate 服务详解

发布时间:2026/5/19 19:01:04

skynet gate 服务详解 gate 服务详解⚠️ 重要说明:本文档讲解的是C gate 服务(service-src/service_gate.c),这是 skynet 的原生 C 服务。目录gate 服务介绍gate 服务的创建gate 服务的作用gate 服务的销毁如何使用 gate 服务总结附录1. gate 服务介绍1.1 什么是 gate 服务gate是 skynet 框架的网络网关服务,负责处理客户端 TCP 连接。它是一个 C 原生服务,提供高性能的网络 I/O 处理。关键特性:监听 TCP 端口,接受客户端连接自动处理 TCP 粘包问题(支持 2/4 字节消息头)支持消息转发到 agent 服务或 watchdog 服务支持 broker 模式(广播消息)支持连接数限制高性能、低延迟1.2 gate 服务在网络架构中的地位┌─────────────────────────────────────────────────────────┐ │ skynet 网络架构 │ └─────────────────────────────────────────────────────────┘ 客户端 1 客户端 2 客户端 N │ │ │ │ TCP 连接 │ │ ↓ ↓ ↓ ┌────────────────────────────────────────────┐ │ gate 服务(网关) │ │ - 监听端口 │ │ - 处理 TCP 连接 │ │ - 粘包处理 │ │ - 消息转发 │ └────────────────────────────────────────────┘ │ │ │ ↓ ↓ ↓ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ agent 1 │ │ agent 2 │ │ agent N │ │ (用户1) │ │ (用户2) │ │ (用户N) │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────┴─────────────────┘ │ ↓ ┌─────────────┐ │ Watchdog │ │ (监控服务) │ └─────────────┘⚠️ 重要:watchdog 的作用必须使用 watchdog 的原因:接收连接事件:当新连接到达时,gate 会向 watchdog 发送"fd open fd addr"消息接收关闭事件:当连接关闭时,gate 会向 watchdog 发送"fd close"消息管理连接生命周期:watchdog 负责创建/销毁 agent 服务如果不使用 watchdog(设置为 “!”):✅ Gate 服务仍然可以工作✅ 连接会被记录在 gate 内部❌Lua 层不会收到任何连接事件通知❌ 无法知道新连接的到达❌ 无法创建对应的 agent 服务❌ 连接处于"未激活"状态源码证据(service_gate.c:260):staticvoid_report(structgate*g,constchar*data,...){if(g-watchdog==0){return;// ⚠️ 没有 watchdog,不发送任何消息}// ... 发送消息给 watchdog}1.3 数据结构1.3.1 connection 结构位置:skynet/service-src/service_gate.c:52structconnection{intid;// socket fd(skynet_socket 管理的 ID)uint32_tagent;// 负责处理该连接的服务 handle(通常是 agent 服务)uint32_tclient;// 客户端标识,用于消息回传时的 sourcecharremote_name[32];// 客户端地址信息(IP:Port)structdatabufferbuffer;// 接收缓冲区,用于处理粘包};1.3.2 gate 结构位置:skynet/service-src/service_gate.c:63structgate{structskynet_context*ctx;// skynet 上下文intlisten_id;// 监听 socket 的 ID,-1 表示未监听uint32_twatchdog;// watchdog 服务的 handle,用于报告连接事件uint32_tbroker;// broker 服务的 handle(广播模式)intclient_tag;// 发送给 agent 的消息类型标签,默认 PTYPE_CLIENTintheader_size;// 消息头大小,2 字节(S 模式)或 4 字节(L 模式)intmax_connection;// 最大连接数structhashidhash;// fd 到连接索引的哈希映射structconnection*conn;// 连接数组structmessagepoolmp;// 消息池(用于 databuffer)};1.4 消息格式1.4.1 客户端发送的消息格式┌──────────────────────────────────────────┐ │ TCP 消息格式 │ ├──────────────────────────────────────────┤ │ │ │ S 模式(2 字节头): │ │ ┌───────────┬──────────────────────┐ │ │ │ 2 字节 │ 消息体 │ │ │ │ 大端序 │ (N 字节) │ │ │ │ 长度 │ │ │ │ └───────────┴──────────────────────┘ │ │ │ │ L 模式(4 字节头): │ │ ┌───────────┬──────────────────────┐ │ │ │ 4 字节 │ 消息体 │ │ │ │ 大端序 │ (N 字节) │ │ │ │ 长度 │ │ │ │ └───────────┴──────────────────────┘ │ │ │ └──────────────────────────────────────────┘消息头说明:S 模式:2 字节大端序,最大消息 65535 字节(64KB)L 模式:4 字节大端序,最大消息 16MB示例:// 发送 3 字节的 "ABC"// S 模式:0x00 0x03 'A' 'B' 'C'// L 模式:0x00 0x00 0x00 0x03 'A' 'B' 'C'1.4.2 服务端发送给客户端的消息格式// 消息格式:数据4 字节 fd(最后 4 字节是目标 socket 的 fd)// 例如:发送 "HELLO" 给 fd=123// 消息:'H' 'E' 'L' 'L' 'O' 0x7B 0x00 0x00 0x00(小端序)1.5 模块导出函数// 创建 gate 实例structgate*gate_create(void);// 初始化 gate 服务intgate_init(structgate*g,structskynet_context*ctx,char*parm);// 释放 gate 实例voidgate_release(structgate*g);2. gate 服务的创建2.1 完整创建流程┌─────────────────────────────────────────────────────────────┐ │ gate 服务创建完整流程 │ └─────────────────────────────────────────────────────────────┘ 阶段一:服务创建(skynet_context_new) ┌───────────────────────────────────────────────────────────┐ │ 1. 查询模块 │ │ skynet_module_query("gate") │ │ ↓ │ │ 查找 gate_create 函数 │ │ │ │ 2. 创建实例 │ │ inst = gate_create() │ │ ↓ │ │ 分配 gate 结构体 │ │ 初始化成员变量 │ │ listen_id = -1 │ │ │ │ 3. 创建服务上下文 │ │ ctx = skynet_malloc(sizeof(*ctx)) │ │ ↓ │ │ 设置模块、实例、回调 │ │ │ │ 4. 注册服务句柄 │ │ handle = skynet_handle_register(ctx) │ │ ↓ │ │ 分配唯一的服务地址 │ │ │ │ 5. 创建消息队列 │ │ queue = skynet_mq_create(handle) │ │ ↓ │ │ 创建服务专属的消息队列 │ └───────────────────────────────────────────────────────────┘ ↓ 阶段二:服务初始化(gate_init) ┌───────────────────────────────────────────────────────────┐ │ 6. 解析参数 │ │ sscanf(parm, "%c %s %s %d %d", ...) │ │ ↓ │ │ header: 'S' 或 'L' │ │ watchdog: 服务名 │ │ binding: 监听地址 │ │ client_tag: 消息类型标签 │ │ max: 最大连接数 │ │ │ │ 7. 查找 watchdog 服务 │ │ g-watchdog = skynet_queryname(ctx, watchdog) │ │ ↓ │ │ 获取 watchdog 服务地址 │ │ │ │ 8. 初始化连接管理 │ │ hashid_init(g-hash, max) │ │ g-conn = skynet_malloc(max * sizeof(struct connection))│ │ ↓ │ │ 创建哈希表和连接数组 │ │ │ │ 9. 设置消息回调 │ │ skynet_callback(ctx, g, _cb) │ │ ↓ │ │ 注册消息处理函数 │ └───────────────────────────────────────────────────────────┘ ↓ 阶段三:开始监听 ┌───────────────────────────────────────────────────────────┐ │ 10. 解析监听地址 │ │ 格式:"host:port" 或 "port" │ │ ↓ │ │ 例如:"127.0.0.1:8888" 或 "8888" │ │ │ │ 11. 创建监听 socket │ │ g-listen_id = skynet_socket_listen(ctx, host, port, BACKLOG)│ │ ↓ │ │ 创建 TCP 监听 socket │ │ │ │ 12. 开始接受连接 │ │ skynet_socket_start(ctx, g-listen_id) │ │ ↓ │ │ 开始监听客户端连接 │ └───────────────────────────────────────────────────────────┘ ↓ 阶段四:服务就绪 ┌───────────────────────────────────────────────────────────┐ │ 13. 加入全局消息队列 │ │ skynet_globalmq_push(queue) │ │ ↓ │ │ 服务开始接收和处理消息 │ │ │ │ 14. 等待客户端连接 │ │ 监听端口,等待客户端连接 │ └───────────────────────────────────────────────────────────┘2.2 关键步骤详解2.2.1 创建 gate 实例位置:skynet/service-src/service_gate.c:81structgate*gate_create(void){structgate*g=skynet_malloc(sizeof(*g));memset(g,0,sizeof(*g));g-listen_id=-1;// 初始化为无效值returng;}关键点:分配 gate 结构体初始化所有成员为 0listen_id设置为 -1,表示未监听2.2.2 初始化 gate 服务位置:skynet/service-src/service_gate.c:549intgate_init(structgate*g,structskynet_context*ctx,char*parm){if(parm==NULL)return1;intmax=0;intsz=strlen(parm)+1;charwatchdog[sz];charbinding[sz];intclient_tag=0;charheader;// 解析参数// 格式:header watchdog binding [client_tag] [max]intn=sscanf(parm,"%c %s %s %d %d",header,watchdog,binding,client_tag,max);if(n4){skynet_error(ctx,"Invalid gate parm %s",parm);return1;}if(max=0){skynet_error(ctx,"Need max connection");return1;}if(header!='S'header!='L'){skynet_error(ctx,"Invalid data header style");return1;}// 设置消息类型标签if(client_tag==0){client_tag=PTYPE_CLIENT;}// 查找 watchdog 服务if(watchdog[0]=='!'){g-watchdog=0;// 不使用 watchdog}else{g-watchdog=skynet_queryname(ctx,watchdog);if(g-watchdog==0){skynet_error(ctx,"Invalid watchdog %s",watchdog);return1;}}g-ctx=ctx;// 初始化连接管理hashid_init(g-hash,max);g-conn=skynet_malloc(max*sizeof(structconnection));memset(g-conn,0,max*sizeof(structconnection));g-max_connection=max;inti;for(i=0;imax;i++){g-conn[i].id=-1;// 初始化为无效 ID}g-client_tag=client_tag;g-header_size=header=='S'?2:4;// 设置消息回调skynet_callback(ctx,g,_cb);// 开始监听returnstart_listen

相关新闻