14 个符号 · 签名与 xsbase.h 逐条核对

契约符号参考

宿主在 TCC 编译产物中按符号名查找回调,全部可选导出、按需声明。符号名即协议——没有注册函数、没有基类、没有宏。

全局契约

视图借用

回调参数中的指针 / 视图(XS_HttpReq、头结构、消息视图)只在回调栈内有效;值可带出,指针必须先拷贝。

Unit 至多一次

ServiceUnit 在一代的生命周期内至多执行一次;未初始化完成的候选不会误调 Unit。

全部可选

14 个符号按需导出。不导出 RequestProc 的 host 就是纯静态站;未导出 WsText/WsBinary 的 ws host 返回 503。

线程分布一览:ServiceInit / ServiceSwap 在装配或 reload 控制线程;RequestProc / Ws* / Event* 在引擎 worker 线程(可并发);ServiceUnit 在 generation GC 线程。跨线程共享状态自行同步。

生命周期回调(3)

ServiceInit 生命周期

本代脚本装载完成后调用一次——建路由表、开数据库、初始化静态状态的地方。

void ServiceInit(XS_HostInfo* pHost);
时机:新代发布前 线程:装配 / reload 控制 导出:可选

新代必须 Init 完成才对外可见;Init 里取交接数据用 xsSwapTake(见 XServer API)。

示例 · 实机验证出处:demo-single/main.c
static void ServiceInit(XS_HostInfo* pHost)
{
    DB_Init();          /* 打开 SQLite、建表 */
    Page_Init();
    Template_Init();
    RouteHTTP_Init();   /* 注册并编译路由 */
}

ServiceUnit 生命周期

本代退役时的析构——按 Init 的逆序释放。全生命周期至多执行一次。

void ServiceUnit(XS_HostInfo* pHost);
时机:旧代引用归零后(GC 线程) 保证:至多一次 导出:可选

触发点有两个:进程优雅停机路径,或热重载后旧代最后一个租约归还。Init 未完成过的代不会调用 Unit。

示例 · 实机验证出处:demo-single/main.c(逆序释放)
static void ServiceUnit(XS_HostInfo* pHost)
{
    RouteHTTP_Unit();   /* 释放 pattern 与路由表 */
    Template_Unit();
    Page_Unit();
    DB_Unit();          /* 关闭数据库 */
}

ServiceSwap 生命周期

换代前导出跨代状态的无破坏快照;新代 ServiceInit 内经 xsSwapTake 取回。

bool ServiceSwap(XS_HostInfo* pHost, xvalue** ppShared);
时机:越过不可逆提交点后 返回:true = 快照有效可提交 导出:可选
参数方向说明
pHost输入即将退役的旧代主机
ppShared输出接收 xvalue* 快照;所有权移交宿主转交新代

无破坏的含义:导出快照后旧代仍可能继续服务(极端情况下提交还会被取消)——不要在 Swap 里关闭资源。

示例 · 契约模式
static bool ServiceSwap(XS_HostInfo* pHost, xvalue** ppShared)
{
    xvalue* pState = xrtValueObject();
    xrtValueObjectSetNew(pState, XRT_STR_LITERAL("counter"),
        xrtValueInt64(G_iCounter));
    *ppShared = pState;
    return true;
}

HTTP 回调(1)

RequestProc HTTP

每个 HTTP 请求的入口。返回三态声明"谁负责这个请求的余生"。

XS_RequestResult RequestProc(XS_HttpReq* pReq);
时机:请求头解析完成后 线程:引擎 worker(可并发) 导出:可选(缺省 = 纯静态 host)
返回值语义驱动随后行为
XS_OK(0)完整响应已写出消费 body 余量,继续 keep-alive
XS_FALLBACK(1)未处理GET/HEAD 落静态层,其余 404
XS_TAKEOVER(2)接管整条连接撤出驱动;应用自行收发并最终 Close

XS_HttpReq 是视图结构体:tcp/tls 二选一非空(xrt 全套流 API 可用)、head 解析后的请求头(含 MethodCode)、body 分帧状态机、host/server 归属。字段逐条见 结构体参考(规划中)。

示例 · 实机验证出处:demo-single/route_http/basic.h
/* demo 的路由框架最终都收敛到 RequestProc */
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
    if ( RouteHTTP_Dispatch(pReq) ) return XS_OK;
    return XS_FALLBACK;   /* 未命中 → wwwroot */
}

WebSocket 回调(6)

XServer 完成握手校验与 xrtWsStreamAttach;掩码、分片重组、控制帧、permessage-deflate 全部在 xrt 层处理。发送用 xrtWsStreamText / Binary 族。

WsOpen WebSocket

连接建立(101 完成、流附加后)触发。

void WsOpen(XS_HostInfo* pHost, xwsstream* pWs);
线程:worker导出:可选
示例
static void WsOpen(XS_HostInfo* pHost, xwsstream* pWs)
{
    (void)xrtWsStreamText(pWs, xrtStrView("connected"));
}

WsText WebSocket

整条文本消息重组完成后触发;tText 为本回调内有效的视图。

void WsText(XS_HostInfo* pHost, xwsstream* pWs, xstrview tText);
线程:worker未导出:该 host 返回 503
示例 · echo
static void WsText(XS_HostInfo* pHost, xwsstream* pWs, xstrview tText)
{
    (void)xrtWsStreamText(pWs, tText);
}

WsBinary WebSocket

整条二进制消息触发;累计超过 ws_message_limit 以 close 1009 结束连接。

void WsBinary(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
线程:worker未导出:该 host 返回 503

WsPing WebSocket

收到 Ping 帧。Pong 的自动回应由 xrt 层处理——此回调用于业务观察(心跳统计等)。

void WsPing(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
线程:worker导出:可选

WsPong WebSocket

收到 Pong 帧——测量往返延迟的位置。

void WsPong(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
线程:worker导出:可选

WsClose WebSocket

连接关闭;携带状态码与最多 123 字节 UTF-8 原因视图。

void WsClose(XS_HostInfo* pHost, xwsstream* pWs, uint16 iCode, xstrview tReason);
线程:worker导出:可选
常见 iCode含义
1000正常关闭
1009消息超限(ws_message_limit)
1011服务端内部错误(如 OOM)

TCP 流回调(3)

XS_StreamConn 是视图结构体:tcp 与 tls 二选一非空,按非空一侧用对应发送 API(tcps 下禁止绕过 tls 直写)。连接生命周期由宿主持有——应用不负责 Destroy。

EventOpen TCP

流连接建立后触发(发欢迎横幅的时机)。

void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn);
线程:worker导出:可选
示例 · 横幅
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn)
{
    if ( pConn->tcp != NULL )
        (void)xrtNetStreamSend(pConn->tcp, "hello\n", 6);
}

EventData TCP

数据到达。pBuffer 是零拷贝缓冲链(回调内借用),尺寸查询 xrtNetBufSize,数据经 xrt 缓冲 API 消费。

void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer);
线程:worker借用:pBuffer 仅回调内有效

EventClose TCP

连接关闭终态;含网络结果码与可选错误对象(借用)。

void EventClose(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetresult iResult, const xerror* pError);
线程:worker资源:连接由宿主析构

UDP 回调(1)

EventDgram UDP

每个数据报触发一次;无连接状态,每个报文都经监听槽位取当前代 lease。

void EventDgram(XS_HostInfo* pHost, xnetudp* pUdp, const xnetudpmessage* pMsg);
线程:worker借用:pMsg 仅回调内有效
pMsg 字段说明
Remote来源地址(xrtNetAddr 族转换文本)
Data / Size负载与长度
FlagsXNET_UDP_MESSAGE_TRUNCATED 等
示例 · 回发来源
static void EventDgram(XS_HostInfo* pHost, xnetudp* pUdp,
    const xnetudpmessage* pMsg)
{
    (void)xrtNetUdpSendTo(pUdp, &pMsg->Remote, "ack", 3);
}