模块契约Proxy

所有权

  • xnetproxyconfig 和 xnetproxyhandshakeconfig 中的视图在创建调用期间借用。
  • xnetproxy 不可变并使用共享引用。
  • xnetproxyhandshake 唯一所有,使用 xrtNetProxyHandshakeDestroy 销毁。
  • xnetproxydial 使用共享引用;运行阶段持有自己的内部引用,用户使用 xrtNetProxyDialRef 和 xrtNetProxyDialDestroy 管理外部引用。
  • 托管拨号成功时完成回调接管 Stream;失败时组合层回收内部 Stream,不向用户暴露半成品。
  • xnetproxyhandshakeconfig.Pool 只借用;非空时必须比握手对象存活更久。
  • xrtNetProxyHandshakeOutput、xrtNetProxyInfo 和 xrtNetProxyHandshakeBound 返回的视图都不能越过其所有者生命周期。

模块契约TCP 传输 API

回调与线程

一个 Stream 的全部回调都在其所属 Worker 上串行执行,不会并发进入同一 Stream。xrtNetStreamWorker 返回 Stream Worker,xrtNetListenerWorker 返回 Listener Worker;回调内可用 xrtNetWorkerIsCurrent 验证线程归属。

事件含义:

  • Open:连接和地址已经发布,可以查询本地、远端地址并开始协议状态机。
  • Read:收到新字节。pBuffer 借用 Stream 的累积接收缓冲,只在回调期间操作。
  • End:对端发送 FIN,读方向结束;写方向仍可继续。
  • HighWater:发送预算第一次达到或越过高水位。
  • LowWater:越过高水位后,预算回落到低水位或以下。
  • Drain:发送预算回到零;一次新的非空队列周期最多产生一次 Drain。若 LowWater 回调内立即加入新数据,Drain 会等重入发送结束后再按最终空队列合并发布,不会从嵌套写入和外层写入重复进入。
  • Close:唯一终态。pError 只在回调期间借用;正常关闭为 XNET_RESULT_OK, NULL。

Read 回调必须消费已处理字节,通常使用 xrtNetBufConsume、xrtNetBufRead 或协议解析器。允许保留未完成帧的前缀;缓冲达到 ReadLimit 时 Stream 自动停止继续接收,消费后低于上限会自动恢复。长期不消费会保持有界读背压,但不会因一次完整缓冲而断开连接。

Accept 在新 Stream 的目标 Worker 上执行。返回 true 表示接受连接,并接管传入的一个调用方引用;随后才发布 Stream Open。返回 false 表示拒绝,XRT 静默关闭 Stream,调用方不得保存或释放该引用。

Error 和 Listener Close 在 Listener 所属 Worker 上执行。Error 表示可观察的接受或初始化失败,不等同于用户拒绝;错误对象只在回调期间借用。

裁剪与依赖

宏依赖能力
XRT_FEATURE_NET_PROXYXRT_FEATURE_NET不可变代理端点和凭据
XRT_FEATURE_NET_PROXY_HANDSHAKEProxy、Net Buffer传输无关的增量握手框架
XRT_FEATURE_NET_PROXY_SOCKS5Proxy HandshakeSOCKS5 CONNECT 和用户名密码认证
XRT_FEATURE_NET_PROXY_HTTP_CONNECTProxy Handshake、HTTP/1 Head、Base64HTTP CONNECT 和 Basic 认证
XRT_FEATURE_NET_PROXY_DIALProxy Handshake、TCP Dial托管 DNS、TCP 与代理握手的完整拨号生命周期

代理协议层不依赖 TCP、TLS、HTTP 客户端或 WebSocket。调用方可以把同一个握手对象接到 XRT TCP、自定义 Socket、测试传输或其他双向字节流。

代理对象

xnetproxyconfig 的所有视图只在 xrtNetProxyCreate 调用期间借用。创建成功后,xnetproxy 在一块精确分配中持有主机、用户名和密码的深拷贝,可以跨线程和请求共享。

XNET_PROXY_AUTH_AUTO 是默认策略:没有凭据时规范化为 NONE,存在凭据时规范化为 REQUIRED。SOCKS5 的 OPTIONAL 会同时提供匿名和用户名密码方法;HTTP CONNECT 的 REQUIRED 与 OPTIONAL 都预先发送 Basic 字段,避免在同一 TCP 连接上隐式重放 CONNECT。HTTP Basic 用户名不能包含冒号,密码允许任意字节。

xrtNetProxyRetain 和 xrtNetProxyRelease 管理共享引用。最后一个引用释放前会清零整个对象分配,避免凭据留在堆内存中。

增量握手

xnetproxyhandshake 由一个传输执行上下文独占驱动。创建时会深拷贝目标主机、增加代理引用并立即生成首个协议报文。

状态驱动规则:

  • XNET_PROXY_HANDSHAKE_WRITE:用 xrtNetProxyHandshakeOutput 借用当前连续输出,真正写出的字节数交给 xrtNetProxyHandshakeSent。部分写入受支持。
  • XNET_PROXY_HANDSHAKE_READ:把网络数据追加到调用方自己的 xnetbuf,再调用 xrtNetProxyHandshakeStep。
  • XNET_PROXY_HANDSHAKE_READY:隧道已经建立。SOCKS5 可以读取 xrtNetProxyHandshakeBound;HTTP CONNECT 没有绑定端点,该函数返回 XERR_NOT_FOUND。握手只消费输入链中的协议回复前缀,同包到达的应用数据仍留在原输入链中。
  • XNET_PROXY_HANDSHAKE_ERROR:使用 xrtNetProxyHandshakeError 读取握手捕获的结构化错误,使用 xrtNetProxyHandshakeCode 读取已经收到的 SOCKS5 线路回复码或 HTTP 状态码。

xrtNetProxyHandshakeOutput 只借用握手对象当前输出的首段。没有待发送数据或参数无效时,它返回 false,并把非空的输出参数规范化为 { NULL, 0 },调用方不会误用上一次查询留下的借用指针。

握手输出可能包含用户名和密码。确认发送的输出前缀会在释放或返回缓冲池前清零;销毁握手也会清零尚未发送的输出。

SOCKS5 契约

当前 SOCKS5 后端实现 RFC 1928 CONNECT 和 RFC 1929 用户名密码认证:

  • 目标支持域名、IPv4 和 IPv6;数字地址直接使用对应线路类型。
  • 用户名和密码各自遵守 255 字节线路上限。
  • 回复支持任意分片和同包合并。
  • 绑定端点支持 IPv4、IPv6 和域名回复。
  • 标准失败码 1 到 8 保留在线路码中,并映射为 XNET_ERROR_PROXY_CONNECT。
  • ReceiveLimit 是硬限制;默认 64 KiB,足以覆盖 SOCKS5,并为后续 HTTP CONNECT Header 提供统一容量契约。

当前 SOCKS5 公共握手只实现 CONNECT。未来的 BIND 和 UDP ASSOCIATE 应作为独立命令能力加入,不会改变 CONNECT 的认证、缓冲和错误契约。

HTTP CONNECT 契约

HTTP CONNECT 后端使用 HTTP/1.1 authority-form 请求,并始终生成与目标一致的 Host 字段:

  • 域名和 IPv4 使用 host:port;IPv6 使用 [address]:port,作用域百分号在线路上编码为 %25。
  • 目标必须是纯主机或数字地址,不能传入 URL、用户信息、路径、查询或片段。
  • 存在凭据时发送 Proxy-Authorization: Basic ...;认证临时区、未发送输出和托管发送副本都会安全清零。
  • 最终任意 2xx 都建立隧道;最多接受 8 个非 101 的 1xx 中间响应,101 明确视为协议错误。
  • 407 映射为 XERR_PERMISSION/XNET_ERROR_PROXY_AUTH,其他拒绝状态映射为 XERR_IO/XNET_ERROR_PROXY_CONNECT,畸形响应保留 xrt.http1 原因链。
  • 响应头增量接收并受 ReceiveLimit 硬限制。只有找到完整空行后才拉直一次,不为字段表分配内存,也不保留代理响应字段。
  • xrtNetProxyHandshakeCode 返回最终 HTTP 状态码;HTTP CONNECT 不虚构 SOCKS5 风格的绑定端点。

托管代理拨号

xrtNetProxyDial 是常用路径:它先复用 TCP Dial 解析并连接代理端点,再在获胜 Stream 的所属 Worker 上驱动传输无关握手。应用只提供最终目标、Stream 事件和一个完成回调,不需要手工搬运握手字节。

Config.Transport 完整保留 TCP Dial 的地址族、候选竞速、Worker 亲和性、连接超时与 Stream 硬边界。Config.Timeout 是代理拨号的全过程上限,覆盖 DNS、TCP 和握手;零值关闭这一层总超时,但不会改写 Transport.Timeout。ReceiveLimit 是握手协议输入硬上限,并且必须不大于 TCP ReadLimit。

托管 Dial 状态与最终目标主机名使用单块拥有分配,不为目标字符串建立第二个堆节点。代理配置、TCP Dial、协议握手和最终 Stream 仍保持独立引用,因为它们具有不同终态和公开生命周期;组合层只消除相同所有权边界内的重复分配,不用对象拼接换取脆弱的隐式依赖。

通过参数和代理配置校验后,组合层会先取得一份 Engine 初始化租约,再分配 Dial、安装全过程 Timer 并创建底层 TCP Dial。只有 Timer、TCP Dial 或最终 Stream 已经接管 Engine 生命周期后,这份临时租约才会释放;任一初始化步骤失败则在返回前回滚。初始化正在其他线程执行时,xrtNetEngineDestroy() 会以 XERR_STATE 拒绝销毁并保持 Engine 为 RUNNING,不会留下一个尚未返回、却已经引用失效 Engine 的半成品组合对象。

终态回调只在代理传输所属 Worker 发布一次,并且不会从 xrtNetProxyDial 的调用栈直接重入。从其他线程提交时,Worker 仍可以与提交 线程并发执行,回调可能早于调用方保存返回值;必须使用回调参数中的 pDial 识别操作。该参数与错误都只在回调期间借用,跨回调保留 Dial 时先调用 xrtNetProxyDialRef。

终态规则:

  • 成功时先安装最终用户事件,再按 Open、已预读 Read、完成回调的顺序发布。完成回调接管一个已经建立隧道的 xnetstream* 引用。
  • 失败、超时和取消时 Stream 参数为空,错误参数只在回调期间借用;Dial 对象保留完整错误原因链,可用 xrtNetProxyDialError 继续查询。
  • xrtNetProxyDialDestroy 只释放调用方持有的 Dial 引用,不等同于取消。放弃未完成操作时先调用 xrtNetProxyDialCancel,再释放引用。

xrtNetProxyDial 的非空返回值包含一份调用方引用。借用的回调参数不是额外 引用,不能无条件销毁;只有调用方已经同步取得返回引用,或者回调先显式增加 引用后,才能在对应所有权路径释放 Dial。

xrtNetProxyDialCancel 可从任意线程调用并且只允许第一次成功。取消会协作终止当前 DNS、候选 TCP 或握手 Stream;所有 Stream 操作仍被投递到其所属 Worker,不会从取消线程直接访问 Worker 私有缓冲。总超时复用同一取消路径,但终态规范化为 XNET_RESULT_TIMEOUT 和 XERR_TIMEOUT。

握手输出按 TCP WriteLimit 的剩余预算分段提交。短写、高低水位和同步 LowWater/Drain 重入都由组合层串行折叠;认证输出的托管副本在离开发送队列时安全清零。只有完成代理回复所需的字节会被消费,同包到达的应用数据仍留在 Stream 读缓冲,并在最终 Read 中原样交付。

xrtNetProxyDialState 区分 RESOLVING、CONNECTING、HANDSHAKE 和三个终态;xrtNetProxyDialStats 同时返回代理阶段与底层 TCP Dial 候选统计。DNS、TCP 和代理协议错误保留原始 cause,外层统一使用 xrt.net 代理错误码表达失败阶段。OOM 时如果连外层组合错误也无法分配,顶层可以直接是静态 XERR_MEMORY;调用方应使用 xrtErrorIs(error, XERR_MEMORY) 检查整条原因链。

托管路径同时支持已经编译进依赖闭包的 SOCKS5 CONNECT 和 HTTP CONNECT。只编译其中一个协议时,另一个类型会明确返回 XERR_UNSUPPORTED;XRT_FEATURE_NET_PROXY_DIAL 本身不强制携带任何具体代理协议,保持裁剪边界清晰。

需要端到端 TLS 时,启用 XRT_FEATURE_TLS_STREAM_DIAL_PROXY 并调用 xrtTlsDialProxy();该模块闭包显式包含 TLS Stream Dial 与 Proxy Dial。该入口 复用本节的代理拨号器建立隧道,但由外层 TLS Dial 统一拥有全过程期限、取消终态、错误链和传输统计。xtlsdialconfig 不嵌入代理 指针;直连 xrtTlsDial() 与代理 xrtTlsDialProxy() 是两个显式入口,以保持配置 ABI 不随功能宏改变,并避免裁剪构建静默退化为直连。

托管代理层不识别端口后端。select、IOCP 与 io_uring 共用同一份 SOCKS5、HTTP CONNECT、并发取消、OOM 回收和单头拨号断言;后端测试入口只选择 xnetportkind。新增端口实现时,应先通过 TCP Dial 契约,再直接复用这些组合测试,不能在代理状态机中增加平台分支。

模块边界

模块裁剪宏作用
httpXRT_MODULE_HTTPtoken、字段、方法、状态和 Content-Length
http_paramXRT_MODULE_HTTP_PARAM参数与 quoted-string 语法
http_param_hostXRT_MODULE_HTTP_PARAM_HOST参数解码后的无分配 Host 验证
http_expectXRT_MODULE_HTTP_EXPECTExpect 字段
http_upgradeXRT_MODULE_HTTP_UPGRADEUpgrade 字段与写出
http_teXRT_MODULE_HTTP_TETE 与 transfer-coding 参数
http_connectionXRT_MODULE_HTTP_CONNECTIONConnection 选项与持久连接
http_trailerXRT_MODULE_HTTP_TRAILERTrailer 声明和尾字段约束
http_encodingXRT_MODULE_HTTP_ENCODINGAccept-Encoding 与 Content-Encoding
http_decodeXRT_MODULE_HTTP_DECODE流式 gzip/deflate 自动解码
http_hostXRT_MODULE_HTTP_HOSTHost authority
http_targetXRT_MODULE_HTTP_TARGET四种 request-target
http1_headXRT_MODULE_HTTP1_HEAD起始行、Header 解析与写入
http1_netXRT_MODULE_HTTP1_NETTCP 块链 Header 解析与余量保留
http1_tlsXRT_MODULE_HTTP1_TLSTLS 明文块链增量 Header 解析
http1_bodyXRT_MODULE_HTTP1_BODY正文计划、chunked 和 trailer
http1_messageXRT_MODULE_HTTP1_MESSAGE连续内存完整消息便利层

http_decode 才依赖 Inflate,http1_net 才依赖网络块链,http1_tls 才依赖 TLS Stream。 只启用连续内存 HTTP/1 解析不会带入压缩、网络、TLS 或 WebSocket。

借用模型

xhttpfield、xhttp1head、xhttp1message 和所有语法游标都借用调用方输入。 解析器不复制字段名称和值,输入缓冲和字段描述符数组必须覆盖借用视图的使用期。

请求方法同时以原始 xhttp1head.Method 视图和 xhttp1head.MethodCode 枚举发布。 xrtHttpMethodParse 按大小写敏感规则识别 GET、HEAD、POST、PUT、DELETE、CONNECT、 OPTIONS、TRACE 和 PATCH;合法但未内置的方法返回 XHTTP_METHOD_OTHER,非法 token 返回 XHTTP_METHOD_INVALID。每个非零枚举值占用独立 bit,既可以作为实际方法直接 比较,也可以通过位或组合成路由方法集合。XHTTP_METHOD_CRUD 覆盖 GET、POST、PUT、 PATCH 和 DELETE,XHTTP_METHOD_ANY 覆盖全部内置方法及 OTHER;应用仍应保留原始 视图处理具体的扩展方法。

字段描述符允许位于合法的未对齐存储。实现使用安全加载,不要求调用方为了协议 解析调整缓冲布局。

XRT 不再提供动态 Header 容器。需要拥有、修改或索引 Header 的框架应在 xhttp 中建立对象模型;快速路径可以直接使用栈数组或应用自己的存储。

Host 语法

xrtHttpHostParse 保留 Host、可选端口和 IP-literal 分类,任意长度十进制端口在 协议层仍然合法,只有可放入 uint16 的值才带 XHTTP_AUTHORITY_PORT_VALUE。 xrtHttpIpv4Valid 与 xrtHttpIpv6Valid 是无错误副作用的纯谓词,公开同一套严格 IP 文本规则,扩展协议无需复制 Host 内部解析器。

xrtHttpParamHostValid 直接消费 xhttpparam 的语义值。quoted-pair 会在验证时 流式解码,任意长度 reg-name 与 IPvFuture 不进入固定缓冲,也不申请堆内存。

Header 解析

xrtHttp1RequestParse 和 xrtHttp1ResponseParse 接受从消息首字节开始的累计输入:

  • XHTTP1_MORE:输入尚未包含完整 Header;
  • XHTTP1_FIELDS:Header 完整,但字段描述符容量不足,FieldCount 给出需求;
  • XHTTP1_READY:解析完成;
  • XHTTP1_ERROR:协议、限额或参数错误。

解析器只接受 CRLF,拒绝 obs-fold、裸 LF、非法字段名、控制字符、冲突的 Content-Length、歧义 Transfer-Encoding 和非法 Upgrade。xhttp1errorinfo 提供稳定错误码、消息相对偏移和行号。

xhttp1limits 分别限制 Header 总长度、起始行、单字段行和字段数量。默认值面向 公网输入,服务端应按路由策略进一步收紧,而不是无上限增长接收缓冲。

网络热路径使用 xrtHttp1RequestParseBuffer、xrtHttp1ResponseParseBuffer 直接扫描 xnetbuf。只有完整 Header 跨块时才连续化实际 Header 前缀,函数不消费输入,Upgrade 后的余量 保持原位。TLS 对应入口是 xrtHttp1RequestParseTls、xrtHttp1ResponseParseTls;输入不足时它们 通过 xrtTlsStreamReadMore 请求下一段受限明文,Header 完成后仍由调用方消费 Head.Bytes。

正文计划

Header 完成后必须调用:

  • xrtHttp1RequestBodyPlan 处理请求;
  • xrtHttp1ResponseBodyPlan 处理响应,并传入原请求方法。

xhttp1bodyplan 的模式是唯一分帧结论:

  • XHTTP1_BODY_NONE:没有正文;
  • XHTTP1_BODY_FIXED:Content-Length 定长;
  • XHTTP1_BODY_CHUNKED:分块编码;
  • XHTTP1_BODY_CLOSE:由可靠 EOF 定界;
  • XHTTP1_BODY_TUNNEL:CONNECT 或 101 后的字节不再属于 HTTP。

响应计划覆盖 HEAD、成功 CONNECT、1xx、204 和 304。调用方不应仅凭 Content-Length 判断响应正文。

流式接收

xrtHttp1BodyInit 创建无分配 reader。xrtHttp1BodyRead 每次返回一个借用正文 片段、容量请求或终态,并通过 Consumed 精确说明已消费线路字节。

xrtHttp1BodyLimitsInit 的 MaxBody 默认为 UINT64_MAX,因为底层 reader 不拥有、 聚合或分配正文。服务端、代理以及任何会持有正文的上层必须在接收前按路由和内存预算 设置有限上限,不能把该协议层默认值直接作为公网接收策略。

对于 chunked,reader 会验证 chunk-size、扩展语法、CRLF、last-chunk 和 trailer, 并严格拒绝 chunk-size 与分号或 CRLF 之间的空白,避免端点对报文边界产生分歧。 正文视图不包含分块元数据。Received 是应用正文长度,WireBytes 是正文区实际 线路长度。

当返回 XHTTP1_BODY_FIELDS 时,TrailerCount 给出所需描述符数量。调用 xrtHttp1BodyTrailers 绑定足够存储后,用同一输入继续解析,不会丢失进度。

close-delimited 模式只有在传输层给出可靠 EOF 时才传入 bEnd = true。超时、取消、 RST 和普通 EOF 不能混为同一种上层状态。

gzip 自动解码

xrtHttpDecodeCreate 直接读取解析后的字段数组。把 xrtHttp1BodyRead 发布的每个 正文片段交给 xrtHttpDecodeWrite,并只在 body reader 完成时设置 bFinal。

identity 和显式原样回退路径不复制输入。gzip/deflate 路径按 Content-Encoding 逆序流式解码,验证 gzip Header、CRC、ISIZE 和压缩流终态。每个中间层与最终 输出都受同一明文硬限额约束。

详见 HTTP 正文解码。

原始写出

xrtHttp1RequestWrite 和 xrtHttp1ResponseWrite 不添加 Host、Date、Server 或其他 策略字段。空输出用于精确测量,容量不足不会产生半个 Header。

高性能发送建议:

1. 在连接或协程栈上的小缓冲中写 Header; 2. 用 xrtNetStreamSendVec 一次提交 Header 与小正文; 3. 大正文用 xrtNetStreamSendRef、SendRefs、SendTake 或 SendBuffer; 4. chunked 正文只用 xrtHttp1ChunkLineWrite 生成短前缀,数据本身保持引用发送; 5. XNET_RESULT_AGAIN 时等待 writable/drain,不绕过网络队列硬上限。

这条路径只构造必需的线路字节,不创建请求、响应、字典或正文对象。

报文长度

  • xhttp1head.Bytes:起始行、Header 和最终空行的线路长度;
  • xhttp1bodyplan.Length:定长正文长度,仅在 FIXED 模式有效;
  • xhttp1body.Received:已经发布的应用正文长度;
  • xhttp1body.WireBytes:已经消费的正文线路长度;
  • xhttp1message.Wire.Size:连续输入中第一条完整消息的线路总长度;
  • xhttp1message.BodyBytes:移除 chunked 元数据后的正文长度。

chunked 和 close-delimited 消息在完成前不存在可提前得知的总线路长度。

TLS 与 Upgrade

HTTP 不拥有 TLS。http1_tls 只是块链适配器,不创建连接或安全策略;证书验证、SNI、会话恢复和 ALPN 仍属于通用 TLS 模块。它按需连续化实际 Header,不增加连接级固定缓冲。

xrtHttpUpgrade*、xrtHttpConnection* 和 WebSocket 握手函数共同完成 Upgrade 验证。101 后的剩余字节必须原样交给新协议,不能继续送入 HTTP body reader。

完整消息便利层

xrtHttp1RequestMessageParse 和 xrtHttp1ResponseMessageParse 适合已经连续驻留在 内存中的报文、测试和协议网关。它们仍只借用输入,不持有堆对象。

固定长度或 close-delimited 正文可由 xrtHttp1MessageBodyView 直接查看;chunked 正文使用 xrtHttp1MessageBodyCopy 去除分帧。真正的网络热路径应优先使用流式 body reader,避免等待和复制整个消息。

扩展库边界

客户端池、重定向、重试、缓存、认证、Cookie、MIME、Multipart、FormData、SSE、 服务器路由和中间件属于独立发布的 xhttp 扩展;它们不参与 XRT 核心构建、单头包或 兼容性承诺。

分层

XRT_FEATURE_NET_TCP 依赖 XRT_FEATURE_NET_ENGINE,公开头文件为 <xrt/tcp.h>。这一层提供数字地址 TCP 连接、监听、字节流、硬背压和异步回调,不隐式执行 DNS、TLS、代理、协议解析或同步等待。

这种边界允许上层按需组合:

  • 数字地址客户端直接使用 xrtNetStreamConnect。
  • 主机名客户端先使用 Resolver,再把选定地址交给 TCP。
  • HTTP、WebSocket、RPC 和自定义协议直接消费 xnetbuf。
  • XRT_FEATURE_NET_TCP_FUTURE 在同一传输契约上提供 Future;XRT_FEATURE_NET_TCP_SYNC 提供阻塞便利层;协程直接使用通用 Future Await。三条路径不把等待状态机复制进核心 Stream。

TCP 不把 IOCP 设为硬依赖。只启用 XRT_FEATURE_NET_PORT_SELECT 时,连接、监听、收发、背压和关闭均可完整工作;同时启用 IOCP 后,Engine 可选择完成式后端,公开语义不变。

XRT_FEATURE_NET_TCP_FILE 是独立可裁剪层,依赖 TCP 与 File。它只为明文 TCP 增加文件区间发送,不把文件模块拖入普通 TCP,也不会被 TLS 隐式使用。

状态

状态只向前推进,CLOSED 是唯一终态。创建函数返回的对象包含一个调用方引用;运行时另持有一个内部引用,直到唯一 Close 回调结束。调用方必须先请求关闭,再使用 xrtNetStreamDestroy 或 xrtNetListenerDestroy 释放自己的引用。

CLOSED 发布前,Socket、缓冲、在途操作和对象对 Engine 的活动占用都已经终结。看到 CLOSED 后可以立即释放调用方引用并销毁 Engine;Close 回调可能仍在 Worker 上收尾,Engine 销毁会等待该 Worker 正常退出。

Ref/Destroy 可以跨线程使用;Ref 不允许从零计数复活对象,并在引用计数达到 INT32_MAX 时失败:

配置

默认值为:读块建议值 2 KiB、接收硬上限 1 MiB、写高水位 256 KiB、低水位 64 KiB、写硬上限 1 MiB、连接超时 30 秒、ReadMode=XNET_STREAM_READ_ADAPTIVE、启用 TCP_NODELAY。

ReadSize 是每次 Reserve 的最小建议,不是每连接常驻缓冲。空闲 Stream 不持有固定 8K;实际接收块来自所属 Worker 的自适应共享池。ReadLimit 和 WriteLimit 是硬边界,不是通知阈值。

完成式后端有三种读取策略:ADAPTIVE 默认在空闲时用零载荷探针等待可读;真实读取填满块后查询内核接收队列,仅在仍有字节可立即读取时连续预投递,队列排空或发生短读后立刻回到探针。这个边界既保留长流吞吐,也避免恰好填满一个块的短突发让空闲连接长期持有缓冲。DIRECT 始终预投递真实接收缓冲,减少持续大流量下的一次完成往返,代价是每个空闲 Stream 持有一个接收块。PROBE 每次真实接收前都先探测,常驻载荷内存最低,适合长连接和稀疏消息。completion 后端不具备 READ_PROBE 能力时,三者都安全退回直接接收;readiness 后端本身不预投递载荷缓冲,因此不受该选项影响。

Address 支持 IPv4/IPv6 和端口 0;创建成功后用 xrtNetListenerLocal 查询系统分配的端口。Affinity 选择 Listener Worker。完成式后端预投递 AcceptConcurrency 个独立 Accept 槽,终态直接由槽身份 O(1) 回收;readiness 后端把该值作为每次可读事件的批量接受预算。接受后的 Stream 按 Worker 轮转分发。

AcceptQueueLimit 是拉取模式尚未领取连接数的硬上限,默认 256,不能为零。队列直接复用已接受 Stream 内的链接,不为每个连接另分配队列节点。达到上限的新连接会被静默拒绝并计入 Rejected,因此慢速 Accept 消费者不会造成无界内存增长。

跨 Worker 初始化接受结果时使用短生命周期分发节点。该节点不常驻 Listener 或 Stream,终态后归还目标 Worker 的统一小节点缓存;缓存总量由 Engine 的 NodeCacheBytes 控制,关闭缓存时直接回到全局堆。

默认策略在所有平台都拒绝第二个活动绑定:Windows 默认启用 ExclusiveAddress,其他平台默认启用 ReuseAddress 以同时保留服务重启能力。需要多个进程或 Listener 共享同一端口时,调用方必须显式关闭默认选项并启用 ReusePort。

ExclusiveAddress 不能和 ReuseAddress、ReusePort 同时启用。ReusePort 是否可用仍由平台 Socket 层决定,失败会返回结构化错误。

连接与监听

两者都要求 Engine 已运行。Connect 返回后处于 CONNECTING,允许预先发送;成功后回调 Open,超时、取消或失败直接进入 Close,不会先发布 Open。任何连接终态都会立即撤销连接超时 Timer 及其 Stream 引用,不会让已经失败的连接继续占用 Engine 定时器资源。调用方可以在 Connect 返回后立即关闭或 Abort:运行时会先收回尚未执行的启动任务,再发布唯一终态,迟到的启动任务不会重新打开对象或访问已释放内存。

Listen 同步完成 Socket 创建、选项、Bind、Listen 和实际地址查询,因此返回非空时端口已经占用;Accept 的预投递异步开始。配置和事件表都在创建时复制,调用方不需要保持其内存。

Listener 有两种明确的接受模式:设置 Events.Accept 是推送模式;不设置则是有界拉取模式。两种模式不能混用。拉取模式可以非阻塞领取已经排队的连接:

返回空指针表示当前没有可领取连接,关闭状态可用 xrtNetListenerState 区分。成功返回会把队列持有的一个 Stream 引用转移给调用方,最终必须先请求关闭,再调用 xrtNetStreamDestroy。同一个 Listener 的直接轮询与异步 Accept 可以按阶段顺序使用,但不能作为多个并发消费者争抢同一队列;已有异步等待者时直接轮询会返回状态错误。

xrtNetListen 的 pData 只属于 Listener。拉取接受得到的 Stream 不继承该指针;调用方可以继续使用拉取接收,或在 Stream 所属 Worker 上调用 xrtNetStreamSetEvents,为每条连接安装独立事件和数据。推送模式也应在 Accept 回调中完成同样的每连接接管。

文件区间发送

xrtNetStreamSendFile 把文件区间作为发送队列中的普通有序段。它与之前和之后的 Send、SendRef、SendTake、SendBuffer 共用同一 FIFO 顺序、WriteLimit、 高低水位、Drain、取消和关闭契约。区间必须完整落在调用时的文件大小内;零长度是 无操作;超过硬发送预算返回 XNET_RESULT_AGAIN。

成功受理时,XRT 复制原生文件句柄,调用方可以立即 xrtClose(file)。失败不接管 文件对象或句柄。复制句柄会在区间完整发送、Abort、错误或关闭清理时准确释放一次。 Windows IOCP 使用异步 TransmitFile,Linux io_uring 在内核支持时使用异步 IORING_OP_SPLICE,Linux readiness 使用非阻塞 sendfile,macOS 与 FreeBSD 使用 各自 sendfile。完成式后端运行时不具备文件发送 opcode 时,不发布对应能力。

该入口仅用于明文 TCP。TLS 必须把文件数据读入用户态完成加密,HTTP/TLS 组合层应 选择异步文件正文路径,不能把明文 sendfile 绕过加密层。

TCP Server

XRT_FEATURE_NET_TCP_SERVER 在 Listener 之上提供面向服务端的聚合层,公开头文件为 <xrt/tcp_server.h>。它不复制 TCP 收发状态机,只负责多端点绑定、Listener 生命周期、 Accept 汇聚、服务端分发和聚合统计。只需要一个低级 Listener 的代码仍可直接使用 xrtNetListen;需要双栈、多 worker 或稳定服务端手感时使用 xrtNetServerStart:

Server 在全部 Listener 退出后先发布 XNET_SERVER_CLOSED,随后完成关闭 Future 并调用 唯一 Close 回调,因此回调内必然能观察终态;其他线程只看到 CLOSED 时,Close 通知 仍可能正在 Worker 上发布。需要把应用资源释放与通知完成线性化时,应等待 Close 回调或 Server 关闭 Future,不能把状态轮询当作回调屏障。

Listen 是端点 0,Additional[0..AdditionalCount) 依次映射为后续逻辑端点。 启动函数在返回前同步完成全部绑定;任意端点失败都会关闭已经创建的 Listener,并以 XNET_ERROR_SERVER_START 包装原始 Socket、配置或平台错误,不会留下部分可用 Server。 Additional 只在启动调用期间借用,其余配置、事件表和实际本地地址都由 Server 保存。

SharedPort 用于双栈或多地址动态端口:端点 0 使用端口 0 成功绑定后,后续端点继承 同一个实际端口;显式非零端口必须彼此一致。每个连接通过 Accept 回调的 iEndpoint 和上层协议保存的端点索引保持逻辑归属,不需要按本地地址反查配置。

XNET_SERVER_SHARED 为每个逻辑端点创建一个 Listener。XNET_SERVER_REUSE_PORT 为每个端点、每个 Engine Worker 创建一份 Listener,并自动启用本地分发、关闭独占绑定、 启用 ReusePort;平台不支持时启动原子失败。该模式用于内核级负载分发,不是 Windows 或其他缺少 reuse-port 语义平台上的可移植默认值。

Server 同样提供推送和拉取两种互斥使用方式。设置 xnetserverevents.Accept 时,回调 在已接受 Stream 的目标 Worker 上执行;返回 true 会接管一个 Stream 引用,返回 false 由 Server 拒绝并回收。未设置 Accept 回调时,AcceptQueueLimit 是跨全部 Listener 的硬队列上限,xrtNetServerAccept 从统一 FIFO 非阻塞领取连接。队列满时 拒绝新连接而不增长内存。

启用 XRT_FEATURE_NET_TCP_SERVER_FUTURE 后,xrtNetServerAcceptAsync 等待同一 FIFO;启用 XRT_FEATURE_NET_TCP_SERVER_SYNC 后,xrtNetServerAcceptWait 复用 该 Future 和统一 deadline/cancel 桥。Future 取消只撤销本次等待,不关闭 Server; 同步入口不能从任意 Engine Worker 调用。三条路径共享同一连接所有权和错误语义。

xrtNetServerClose 原子停止全部 Listener,丢弃尚未领取的 Stream,并在 Listener、 分发任务和等待者全部退出后发布唯一 Close。已经由应用接管的 Stream 独立存活,Server 不会擅自关闭业务连接。调用方应先关闭 Server,再释放自己的 xnetserver 引用; xrtNetServerListener 返回的低层 Listener 是额外引用,必须独立销毁。

xrtNetServerEndpointCount、xrtNetServerLocal 和 xrtNetServerListenerCount 暴露稳定拓扑;xrtNetServerStats 汇总接受、拒绝、 错误、队列、等待者和已关闭 Listener。完整示例位于 examples/network/tcp_server/main.c,双端点、reuse-port、关闭重入、队列溢出、 OOM、Future、同步和单头真实收发均有独立回归测试。

托管主机连接

启用 XRT_FEATURE_NET_TCP_DIAL 后,主机名解析和多地址连接策略作为独立裁剪层建立在 Resolver 与数字地址 TCP 之上:

xrtNetDialConfigValid 完整校验 Dial 策略及其嵌套 xnetstreamconfig,适合在 HTTP、 WebSocket、RPC 等组合库复制配置前复用同一口径。成功不修改线程错误;失败返回 false 并发布具体参数错误。上层库不应复制 XRT 私有校验逻辑,否则默认值和边界会 随底层演进发生分叉。

Dial 的默认总超时为 30 秒、候选间隔为 250 毫秒、最多尝试 8 个地址。Timeout 从 API 调用时开始计算,包含排队、DNS 和所有连接候选;零表示不设置总截止时间。单个候选仍受 Stream.ConnectTimeout 约束。FallbackDelay == 0 会立即并发启动所有允许的候选,适合调用方明确控制地址数量的低延迟场景。

Resolver 返回顺序决定首选地址族;Dial 保持各地址族内部顺序,并交错 IPv6/IPv4 候选。第一个候选立即启动,仍未成功时按 FallbackDelay 逐个增加并发候选;候选同步失败会立即补充下一个地址。MaxAttempts 是复制到 Dial 内的硬上限,既限制连接放大,也限制每次操作的地址内存。

Dial 直接从 Resolver 的不可变结果交错复制一份有界候选数组并写入目标端口,不创建中间地址列表。Dial 状态与拥有的主机名使用单块分配;每个活动连接候选只建立一个短生命周期节点,并在释放 Stream 持有后归还所属 Worker 的统一小节点缓存。缓存受 Engine NodeCacheBytes 硬上限约束,关闭缓存后语义不变并直接使用全局堆。

xrtNetDial 只在入口调用期间借用 Resolver;成功创建的 xnetresolveop 独立承担后续解析生命周期。Resolver 的销毁操作会在尚有解析任务时失败,调用方应在全部解析工作排空后销毁它。Dial 取消、超时或提前失败时会从尚未开始的 Resolver 回调原子分离;不可中断的系统解析函数可以继续在 Resolver Worker 中收尾,但不再持有 Dial 或 Engine。Dial 的 Timer、候选和 Engine 活动占用全部归零后才发布状态与完成回调,因此失败或取消回调本身就是稳定清理屏障;没有其他活动对象时可以立即销毁 Engine,不需要重试。成功交付的 Stream 是调用方拥有的独立活动对象,仍应先关闭并释放。

完成回调只执行一次,并在 Affinity 选定的网络 Worker 上运行。它不会从 xrtNetDial 的调用栈直接重入;从该 Worker 内提交时,回调一定在当前 Worker 任务返回后发生。从其他线程提交时,Worker 可以与提交线程并发执行,因此回调 可能早于调用方保存返回值。回调必须使用参数中的 pDial 识别操作,且该参数 本身是借用引用;需要跨回调保存时先调用 xrtNetDialRef。成功时满足以下条件:

  • Stream 已经完成用户 Open 回调且状态为 XNET_STREAM_OPEN。
  • pStream 的一个调用方引用转移给完成回调,Dial 不再拥有它。
  • xrtNetDialState(pDial) 已经是 XNET_DIAL_CONNECTED。

xrtNetDial 非空返回值包含一个调用方引用。只有调用方已经同步取得该引用后, 才能在完成回调内释放它;不能把借用的回调参数直接当成一份额外引用销毁。若 回调需要独立保留 Dial,应在回调内先 xrtNetDialRef,再在使用结束后销毁。

失败或取消时 pStream == NULL,pError 是只在回调期间借用的结构化错误。Dial 会保存终态错误,保留 Dial 引用时可继续通过 xrtNetDialError 借用。解析错误以 XNET_ERROR_DIAL_RESOLVE 包装 Resolver 根因;候选错误以 XNET_ERROR_DIAL_CONNECT 包装最后一个端点错误;OOM、超时和取消保留对应 xerrkind,不会统一降级为普通 IO 错误。

取消和成功、失败共用同一个原子终态仲裁门。xrtNetDialCancel 返回 true 表示取消已经赢得竞争,Resolver 操作和全部未公开候选将被取消,唯一完成结果保证为 XNET_RESULT_CANCELLED;返回 false 表示取消没有取得所有权,既不会改写已经保留的获胜连接,也不会产生“取消已受理但随后成功”的矛盾结果。重复取消或对象已经终结时同样返回 false。获胜候选会在公开用户 Open 前保留成功终态,因此 Open 回调内再取消必然返回 false。

统计包含可用地址数、已经启动和失败的候选数、当前/峰值并发数以及获胜索引。它是跨线程无锁快照,只有 HasWinner == true 时 WinnerIndex 才有效。多个计数字段不承诺来自同一个瞬间。

发送与背压

  • Send 在返回前复制一个连续字节区。
  • SendVec 检查总长度溢出,并在返回前聚集复制多个 Span。
  • SendRef 零复制引用外部数据,非空数据必须提供释放过程;零长度是无操作,不转移所有权,也不调用释放过程。
  • SendRefs 原子受理多个零复制引用,适合 Header、固定响应片段和文件块的聚集发送;空片段被忽略,非空片段分别在离队时执行一次释放过程。
  • SendTake 接管由 xrtMalloc 家族取得的非空数据,最终调用 xrtFree;NULL, 0 是无操作,非空指针配零长度属于参数错误且不转移所有权。
  • SendBuffer 在 Stream 所属 Worker 上接管完整 xnetbuf 块链。它只建立发送视图元数据,不复制载荷;适合把 Read 回调缓冲直接转发到另一个方向,或连接 TLS、HTTP 等协议队列。

只有返回 XNET_RESULT_OK 才接受数据和转移 ref/take/buffer 所有权。SendRefs 和 SendBuffer 都是全有或全无操作:元数据或任一发送视图建立失败时,不会释放外部片段、清空源缓冲或留下部分队列和预算。SendBuffer 成功后源缓冲恢复为空但保留原缓冲池,可立即复用;其中的借用块仍要求调用方保持载荷存活到发送队列离队。XNET_RESULT_AGAIN 表示加入后会超过 WriteLimit;调用方保留数据,可以等待 LowWater 或 Drain 后重试。XNET_RESULT_CLOSED 表示写半关闭、关闭或 Abort 已经封闭新发送。XNET_RESULT_ERROR 表示参数、线程归属、活动写预留、溢出、OOM 或命令提交失败,详细原因在当前执行上下文的 xerror。

发送预算在 API 入口原子占用,包含已经受理但尚未链接到 Worker 缓冲的跨线程命令。关闭过程先封闭新发送,再等待已经进入提交区的调用完成,因而不会越过一个已经线性化成功的 Send。

Worker 回调内的 Send/SendVec 直接复制到本 Worker 的拥有型发送缓冲;缓冲池已经有合适缓存块时不依赖全局分配器,也不再建立“载荷节点 + 引用块”两级对象。普通 Send 的其他线程调用通过 Engine 命令队列提交,使用独立拥有节点保证调用返回后源数据即可失效。SendBuffer 只能在 Stream Worker 内调用,因为缓冲池和源块链都是线程归属对象;跨线程调用返回 XNET_RESULT_ERROR/XERR_STATE,不会接管源缓冲。需要跨线程发送时使用复制、ref、refs 或 take 路径,或者先把操作投递到 Stream Worker。

跨线程复制发送的小载荷节点,以及 SendRef、SendRefs、SendTake、SendBuffer 所需的不超过 1 KiB 的短生命周期元数据,共享 Stream 所属 Worker 的小节点缓存。 较大载荷和元数据直接走全局堆,避免为了少数大请求扩大常驻缓存。节点在释放外部 所有权并保存必要生命周期引用后回收,不会发生回收后读取或重复释放。

TCP 是无消息边界的字节流,核心不会把一次 Read 回调解释成一条业务消息,也不会内置聊天室、主题或广播组。需要广播时,应用应先用 Line/Length Frame 建立明确消息边界;连接注册表为每个成员持有一个 Stream 引用,在短锁内建立稳定快照,离开锁后分别发送并释放快照引用。每个成员的 AGAIN、关闭和错误必须独立处理,不能因为部分成员成功便丢弃慢成员的发送所有权,也不能用无限队列掩盖背压。具有消息语义的 WebSocket 连接组与聚合发送状态机属于 xws 扩展层,不在 TCP 或 XRT WebSocket 协议核心中重复实现。

Pending 返回当前已占用的发送预算,不等同于内核 Socket 缓冲字节数。成功短写或 completion 每确认一段前缀,值就按实际发送字节下降;外部 ref/take 的释放过程仍只在所属完整片段离队后调用。WriteLimit 返回创建时固定的发送硬上限,供上层协议判断一条原子输出是否永远能够容纳。Writable 返回 WriteLimit - Pending 的并发快照,供 HTTP、TLS 和自定义协议限制下一段输出;它只是观察值,实际受理结果始终以 Send 返回值为准。

读取控制与关闭

Pause 立即阻止预投递新的接收;完成式后端已经提交的一次接收仍可能到达并产生 Read。 Resume 只能用于读方向仍开放的 Stream,并唤醒所属 Worker。恢复使用 Stream 内嵌命令, 不分配内存;并发调用会合并为一次待执行驱动,但不会丢失最后一次恢复请求。

ShutdownWrite 封闭新发送,排空已接受队列后发送 TCP FIN,读方向继续工作。典型“发送完请求并等待响应”直接使用该函数。

Close 封闭读写入口、停止新读取、排空已经接受的发送队列,然后正常关闭 Socket。它不等待对端 FIN;需要该语义时先 ShutdownWrite,在 End 后调用 Close。

Abort 取消在途 IO、丢弃发送队列并使用异常关闭。被丢弃的 ref/take 数据仍执行释放过程。三种操作都幂等;Abort 可以把尚未完成的普通关闭升级为异常关闭,即使 Close 与 Abort 已经按顺序进入同一 Worker 的生命周期命令队列,最终结果仍为 XNET_RESULT_CANCELLED。

Listener Close 停止接受新连接,取消全部预投递 Accept,并在其终态到达后发布唯一 Close。关闭使用对象内预留命令,不受公开命令容量和运行期分配失败影响;对有效 Listener 的首次和重复关闭都返回 true。已经交给 Accept 回调并返回 true 的 Stream 独立存活。

拉取读取与 Future

不设置 Events.Read 时,Stream 进入拉取模式。核心 TCP 层提供 Worker 内零额外状态机的读取接口:

Available 是可跨线程查询的字节数快照。Buffer、Read 和 Consume 只允许在所属 Worker 内调用;Buffer 返回的只读借用不能保存,也不能绕过 Stream 直接消费。Read 与 Consume 会更新 BufferedBytes、解除 ReadLimit 背压并继续驱动后端接收。

启用 XRT_FEATURE_NET_TCP_FUTURE 后,可以从任意线程登记统一等待:

等待条件是水平条件:连接已经打开、已有可读字节、当前可写、发送预算为零或对象已经关闭时,新登记的 Future 可以立即结束。OPEN 在连接成功后完成,连接超时或失败会保留原始结构化连接错误;READ 和 RecvAsync 要求 Stream 没有 Read 回调,避免两个消费者竞争同一字节流;写、Drain 和 Close 等待可以和推送读取并用。

AcceptAsync 只用于没有 Accept 回调的拉取 Listener。它与非阻塞 Accept 消费同一个有界 FIFO 队列;取消只撤销本次等待,不停止 Listener,也不丢弃随后到达的连接。成功值是 Future 持有的 xnetstream*,只在销毁 Future 前保持该引用;需要让 Stream 独立存活时先调用 xrtNetStreamRef。Listener 关闭会回收所有未领取 Stream,并使待定和迟到的 Accept Future 进入 CLOSED。

RecvAsync 按登记顺序消费当前可用的前缀,iMaxBytes == 0 表示取本次可用的全部字节。成功值是 Future 持有的 xnetbytes;xrtNetBytesView 返回只在继续持有结果引用时有效的借用视图,数据不是零结尾文本。需要让结果独立于 Future 存活时先调用 xrtNetBytesRef,最后以 xrtNetBytesDestroy 释放。结果内存分配失败时 Future 进入 FAILED/XERR_MEMORY,原接收字节保持未消费,可在恢复后重试。

examples/network/tcp_future/main.c 可直接运行,连续演示 Accept、Open、Read、Drain、Recv 和 Close Future;其中 Read 只观察水平可读条件,随后由 Recv 消费字节,Drain 只表示本地发送预算归零。

正常 FIN 且没有剩余字节时,接收 Future 进入 CLOSED;网络错误进入 FAILED;Stream 异常取消进入 CANCELLED。取消一个等待 Future 只移除本次等待,不关闭、半关闭或中断 Stream。Close 等待在正常关闭时解析成功,即使登记发生在对象进入 CLOSED 之后。

通用 Future 已经提供协程 Await,因此 TCP 不导出网络专用 Co API。调度协程直接等待 TCP Future;协程取消只结束本次 Await,不会隐式取消可能共享的 Future。不再需要独占网络操作时,应在清理路径显式调用 xrtFutureCancel。取消成功后 Stream 保持打开并可继续登记新等待。

Stream 与 Listener 的 Future 等待节点是短生命周期对象,分别保持在 128 字节和 64 字节以内,并共享所属 Worker 的 NodeCacheBytes 预算。节点先从等待链线性化 移除,再解除取消监听、归还缓存并释放其持有的网络对象引用,最后发布 Promise; 取消、关闭和就绪竞争仍只有一个路径能够回收节点。活动节点在借用缓存期间持有临时 Engine 租约,终态发布前已经归还缓存并释放租约;已经进入 CLOSED 的 Stream 或 Listener 新建 Future 时不再访问 Worker,即使 Engine 已经销毁,也可以稳定取得 Close、EOF 或 关闭的 Accept 结果。

启用 XRT_FEATURE_NET_TCP_DIAL_FUTURE 后,托管主机连接使用同一套 Future 契约:

成功值是 Future 持有一个调用方引用的 xnetstream*,取值时 Stream 已经处于 OPEN。需要在销毁 Future 后继续使用时,先调用 xrtNetStreamRef。Future 取消会转发给整个 Dial,而不是某一个候选;成功与取消相撞时只发布一个终态,未被 Promise 接受的成功 Stream 会自动 Abort 并释放。解析和连接的结构化原因链原样进入 Future 错误。Future 进入失败或取消终态前,Dial 已经分离迟到的 Resolver 回调并清空 Timer、候选和 Engine 活动占用;成功终态只保留作为公开结果交付的 Stream。

阻塞便利层

XRT_FEATURE_NET_TCP_SYNC 依赖 TCP Future 与通用网络同步桥,只阻塞调用线程,不创建隐藏 Engine、Worker、辅助线程或第二套连接状态机。以下函数都复用已有对象所属 Engine:

这些函数不能从目标 Stream 或 Listener 所属 Worker 调用,否则在阻塞事件循环前以 XERR_STATE 失败并撤销刚建立的 Future。成功接受返回调用方 Stream 引用;成功接收返回调用方 xnetbytes 引用,两者都独立于内部 Future,分别使用 xrtNetStreamDestroy 与 xrtNetBytesDestroy 释放。创建 Future、复制结果或增加结果引用发生 OOM 时不消费连接或接收字节。

同步等待只有一个线性化结果:Future 已经在等待锁内进入终态时,底层操作终态获胜;等待先返回超时或外部取消时,控制原因获胜,同步层立即向 Future 发出协作取消,并以对应 XERR_TIMEOUT 或 XERR_CANCELLED 返回。随后到达的网络终态不会改写本次调用的错误。对象关闭则保留 XERR_CLOSED,底层失败保留完整结构化原因链和 TCP 操作码。

XRT_FEATURE_NET_TCP_DIAL_SYNC 在相同契约上提供主机名连接:

它不创建默认 Resolver 或隐藏 Engine。截止时间和外部取消结束本次阻塞连接并取消整个 Dial;成功返回已经 OPEN 的调用方 Stream 引用。Resolver、候选连接和系统错误的原因链与 xrtNetDialAsync 完全一致。

查询与统计

xrtNetStreamSocket 只在所属 Worker 的 Stream 或 Accept 回调内返回借用 Socket,供高级用户通过 xrtNetSocketSet/Get 或 xrtNetSocketNative 配置标准库尚未覆盖的选项。调用方不能关闭 Socket、改变阻塞模式、重新连接、执行收发或把它注册到另一个事件循环;违反这些约束会破坏 Stream 状态机。返回后不能保存 Socket。

SetEvents 在所属 Worker 上原子替换事件表和用户数据,用于 HTTP Upgrade、WebSocket、自定义协议协商以及分层协议处理器接管连接。Stream 必须已经处于 OPEN;Listener Accept 在公开 Open 之前也满足该条件。切换不会隐式调用新 Open,也不会重放已经留在接收缓冲中的数据;接管层必须在切换点显式取得并处理协议余量。安装 Read 回调时若仍有挂起的读/接收 Future 会返回 XERR_STATE,并保持原事件表不变;Future 登记与该检查由同一把短锁线性化,推送与拉取消费者不能竞态并存。

SetData 同样只允许在 Stream Worker 回调中调用,使协议状态切换与事件处理保持顺序一致。Data 查询以 acquire 语义返回线程安全的借用指针快照,但不延长目标对象生命周期;调用方仍需保证指针目标在使用期间存活。

Listener 用户数据在创建时保存,之后不可替换,因此 xrtNetListenerData 返回稳定的借用指针;它同样不延长目标生命周期,指针所指对象的并发访问仍由调用方同步。

xrtNetStreamError 只在 Stream 到达 CLOSED 后返回导致终止的借用错误;正常关闭或尚未终止返回空。状态的 acquire 读取保证终态错误已经发布。

Stream 统计包含收发字节、Read/Write 完成次数、硬上限拒绝次数、当前/峰值发送预算、当前接收缓冲、自动读背压、写背压和读写终态。Listener 统计区分已接受、用户或队列拒绝、内部错误、内核在途 Accept、跨 Worker 初始化任务、当前/峰值拉取队列和异步等待者;关闭会等待在途 Accept 和初始化任务归零,并把队列与等待者清空。统计是无锁快照,多个字段之间不保证同一瞬间一致。

代理层(proxy.h)

以下各节复用自 proxy.md(tcp.md 与其共享 include/xrt/proxy.h)。

示例与发布门槛

推送模式回环示例位于 examples/network/tcp/main.c,聚合服务端示例位于 examples/network/tcp_server/main.c,拉取与 Future 请求响应示例位于 examples/network/tcp_future/main.c,阻塞 TCP 与托管连接示例位于 examples/network/tcp_sync/main.c、examples/network/tcp_dial_sync/main.c。测试使用同一契约分别验证 select 和 IOCP,并覆盖慢速对端大块发送、高低水位与 Drain、ReadLimit 自动停读和恢复、跨 Worker Accept/Close 排序、有界拉取 Accept 与溢出、多端点共享端口、reuse-port、聚合关闭、连接和 Accept Future、Resolver 合并、缓存命中时的 Worker 内非重入提交、跨线程 Dial 取消、双栈候选竞争、并发连接、结果复制与 Dial OOM、同步超时/取消/关闭竞态、结果 OOM 不消费、协程挂起恢复和单头文件真实收发。

TLS 传输适配已经作为独立 XRT_FEATURE_TLS_STREAM 裁剪单元落地,直接使用 SendBuffer 把完整密文块链移动到 TCP 队列。xrtTlsStreamTransport() 返回的原始 Stream 只供地址、统计和安全 socket 选项查询;直接关闭、收发、替换事件或消费其缓冲会破坏 TLS 组合状态机。更高层协议直接组合 TCP Server 和 TLS Stream,不改变这里的字节流、所有权、背压、等待、Dial 和关闭契约。

xnetproxytype类型

代理类型只描述协议;TCP、TLS 和上层客户端决定如何承载协议。

值语义
XNET_PROXY_SOCKS5SOCKS5 代理
XNET_PROXY_HTTP_CONNECTHTTP CONNECT
typedef enum xnetproxytype { XNET_PROXY_SOCKS5 = 1, XNET_PROXY_HTTP_CONNECT } xnetproxytype;

xnetproxyauth类型

AUTO 在存在凭据时要求认证,否则只允许匿名;OPTIONAL 显式允许降级为匿名。

值语义
XNET_PROXY_AUTH_AUTO自动
XNET_PROXY_AUTH_NONE无
XNET_PROXY_AUTH_REQUIRED需要代理认证
XNET_PROXY_AUTH_OPTIONAL可选认证
typedef enum xnetproxyauth { XNET_PROXY_AUTH_AUTO = 0, XNET_PROXY_AUTH_NONE, XNET_PROXY_AUTH_REQUIRED, XNET_PROXY_AUTH_OPTIONAL } xnetproxyauth;

xnetproxyconfig类型

代理对象持有配置深拷贝;主机不要求零结尾,凭据允许任意字节。

字段类型语义
Typexnetproxytype类型
Hostxstrview主机名
Portuint16端口
AuthxnetproxyauthAuth
UsernamexbytesviewUsername
PasswordxbytesviewPassword
typedef struct xnetproxyconfig { xnetproxytype Type; xstrview Host; uint16 Port; xnetproxyauth Auth; xbytesview Username; xbytesview Password; } xnetproxyconfig;

xnetproxyinfo类型

信息视图由代理对象持有,只能在至少一个对象引用存活时借用。

字段类型语义
Typexnetproxytype类型
Hostxstrview主机名
Portuint16端口
AuthxnetproxyauthAuth
UsernamexbytesviewUsername
PasswordxbytesviewPassword
typedef struct xnetproxyinfo { xnetproxytype Type; xstrview Host; uint16 Port; xnetproxyauth Auth; xbytesview Username; xbytesview Password; } xnetproxyinfo;

xnetproxyhandshakestate类型

握手状态同时告诉传输层下一步应发送、接收还是发布隧道。

值语义
XNET_PROXY_HANDSHAKE_WRITE写方向
XNET_PROXY_HANDSHAKE_READ读方向
XNET_PROXY_HANDSHAKE_READY就绪
XNET_PROXY_HANDSHAKE_ERROR失败
typedef enum xnetproxyhandshakestate { XNET_PROXY_HANDSHAKE_WRITE = 1, XNET_PROXY_HANDSHAKE_READ, XNET_PROXY_HANDSHAKE_READY, XNET_PROXY_HANDSHAKE_ERROR } xnetproxyhandshakestate;

xnetproxyendpoint类型

域名端点使用 Host;数字端点使用 Address,端口始终保存在 Address.Port。

字段类型语义
Addressxnetaddr地址
Hostxstrview主机名
typedef struct xnetproxyendpoint { xnetaddr Address; xstrview Host; } xnetproxyendpoint;

xnetproxyhandshakeconfig类型

输入缓冲池由调用方借用,并且必须比握手对象存活更久。

字段类型语义
Proxyconst xnetproxy*Proxy
TargetHostxstrviewTargetHost
TargetPortuint16TargetPort
ReceiveLimitsize_tReceiveLimit
Poolxnetbufpool*Pool
typedef struct xnetproxyhandshakeconfig { const xnetproxy* Proxy; xstrview TargetHost; uint16 TargetPort; size_t ReceiveLimit; xnetbufpool* Pool; } xnetproxyhandshakeconfig;

xnetsocks5reply类型

SOCKS5 CONNECT 回复码保留 RFC 1928 的线路值,便于日志和策略判断。

值语义
XNET_SOCKS5_SUCCEEDED成功
XNET_SOCKS5_GENERAL_FAILURE通用失败
XNET_SOCKS5_RULESET_DENIED被规则集拒绝
XNET_SOCKS5_NETWORK_UNREACHABLE网络不可达
XNET_SOCKS5_HOST_UNREACHABLE主机不可达
XNET_SOCKS5_CONNECTION_REFUSED连接被拒绝
XNET_SOCKS5_TTL_EXPIREDTTL 过期
XNET_SOCKS5_COMMAND_UNSUPPORTEDCOMMAND不支持
XNET_SOCKS5_ADDRESS_UNSUPPORTED地址类型不支持
typedef enum xnetsocks5reply { XNET_SOCKS5_SUCCEEDED = 0, XNET_SOCKS5_GENERAL_FAILURE = 1, XNET_SOCKS5_RULESET_DENIED = 2, XNET_SOCKS5_NETWORK_UNREACHABLE = 3, XNET_SOCKS5_HOST_UNREACHABLE = 4, XNET_SOCKS5_CONNECTION_REFUSED = 5, XNET_SOCKS5_TTL_EXPIRED = 6, XNET_SOCKS5_COMMAND_UNSUPPORTED = 7, XNET_SOCKS5_ADDRESS_UNSUPPORTED = 8 } xnetsocks5reply;

xnetproxydialstate类型

Proxy Dial 状态区分代理端点解析、TCP 连接和协议握手。

值语义
XNET_PROXY_DIAL_RESOLVING解析中
XNET_PROXY_DIAL_CONNECTING连接中
XNET_PROXY_DIAL_HANDSHAKE握手阶段
XNET_PROXY_DIAL_CONNECTED已连接
XNET_PROXY_DIAL_FAILED已失败
XNET_PROXY_DIAL_CANCELLED已取消
typedef enum xnetproxydialstate { XNET_PROXY_DIAL_RESOLVING = 0, XNET_PROXY_DIAL_CONNECTING, XNET_PROXY_DIAL_HANDSHAKE, XNET_PROXY_DIAL_CONNECTED, XNET_PROXY_DIAL_FAILED, XNET_PROXY_DIAL_CANCELLED } xnetproxydialstate;

xnetproxydialconfig类型

Timeout 覆盖 DNS、TCP 和代理握手全过程;零值保留各内层超时。

字段类型语义
TransportxnetdialconfigTransport
Timeoutint64超时(毫秒)
ReceiveLimitsize_tReceiveLimit
typedef struct xnetproxydialconfig { xnetdialconfig Transport; int64 Timeout; size_t ReceiveLimit; } xnetproxydialconfig;

xnetproxydialstats类型

Proxy Dial 保持底层 TCP Dial 统计,并补充当前协议阶段。

字段类型语义
Statexnetproxydialstate状态
TransportxnetdialstatsTransport
typedef struct xnetproxydialstats { xnetproxydialstate State; xnetdialstats Transport; } xnetproxydialstats;

xnetproxy类型

不可变代理端点可以跨请求和线程共享。 不透明句柄或别名;生命周期与所有权见各使用方 API 节。

typedef struct xnetproxy xnetproxy;

xnetproxyhandshake类型

单个握手由一个传输执行上下文独占驱动。 不透明句柄或别名;生命周期与所有权见各使用方 API 节。

typedef struct xnetproxyhandshake xnetproxyhandshake;

xnetproxydial类型

托管代理拨号对象(不透明):内部串联名称解析、TCP 连接与代理握手。 托管代理拨号对象(不透明):内部串联名称解析、TCP 连接与代理握手,完成时经回调移交 Stream。 不透明句柄或别名;生命周期与所有权见各使用方 API 节。

typedef struct xnetproxydial xnetproxydial;

xnetproxydialproc类型

完成回调在代理传输 Worker 上至多执行一次,不会从提交调用栈重入。 pDial 和 Error 只在回调期间借用;成功回调接管隧道 Stream 引用。 回调类型;参数与返回语义见签名及各使用方 API 节。

typedef void(*xnetproxydialproc)( xnetproxydial* pDial, xnetresult Result, xnetstream* pStream, const xerror* pError, ptr pData)
代理对象

xrtNetProxyConfigInit函数

初始化 SOCKS5、自动认证且没有固定容量字段的代理配置。

void xrtNetProxyConfigInit(xnetproxyconfig* pConfig)

参数

参数方向约束说明
pConfig输出非空接收配置
返回值
返回含义失败时状态
无已初始化—
错误
  • 无 — 初始化不失败

范例已编译验证

proxy_tour · 默认配置

	xrtNetProxyConfigInit(&ProxyConfig);

xrtNetProxyCreate函数

深拷贝代理端点和凭据,创建可跨线程共享的不可变对象。

xnetproxy* xrtNetProxyCreate(const xnetproxyconfig* pConfig)

参数

参数方向约束说明
pConfig输入非空且通过校验代理配置
返回值
返回含义失败时状态
非空代理对象(引用 1)—
NULL创建失败xrt.net 域错误
错误
  • xrt.net / XNET_ERROR_PROXY_CONFIG(XERR_ARGUMENT / XERR_VALUE) — 配置字段非法或组合不支持
  • xrt.net / XNET_ERROR_PROXY_CREATE — 对象或内部缓冲分配失败(XERR_MEMORY 等)

范例已编译验证

proxy_tour · 创建代理对象

	pProxy = xrtNetProxyCreate(&ProxyConfig);

xrtNetProxyRetain函数

增加代理对象引用并返回原指针。

xnetproxy* xrtNetProxyRetain(const xnetproxy* pProxy)

参数

参数方向约束说明
pProxy输入非空目标代理
返回值
返回含义失败时状态
非空原指针,引用 +1—
NULL参数非法XERR_ARGUMENT
错误
  • XERR_ARGUMENT — 指针为空或参数非法

范例已编译验证

proxy_tour · 共享引用

		((pRetained = xrtNetProxyRetain(pProxy)) != pProxy) ) {

xrtNetProxyRelease函数

释放代理对象引用;最后一个引用会清零整块配置存储。

void xrtNetProxyRelease(xnetproxy* pProxy)

参数

参数方向约束说明
pProxy输入允许空空 = 空操作
返回值
返回含义失败时状态
无引用 -1—
错误
  • 无 — 释放不失败

范例已编译验证

proxy_tour · 释放引用

	xrtNetProxyRelease(pRetained);

xrtNetProxyInfo函数

复制代理对象的只读信息视图。

bool xrtNetProxyInfo(const xnetproxy* pProxy, xnetproxyinfo* pInfo)

参数

参数方向约束说明
pProxy输入非空目标代理
pInfo输出非空接收信息快照
返回值
返回含义失败时状态
true快照已复制—
false参数非法XERR_ARGUMENT
错误
  • XERR_ARGUMENT — 指针为空或参数非法

范例已编译验证

proxy_tour · 信息视图

	if ( !xrtNetProxyInfo(pProxy, &Info) ||
		(Info.Type != XNET_PROXY_SOCKS5) ||
		(Info.Host.Size != 12u) ||
		(memcmp(Info.Host.Data, "socks5.local", 12u) != 0) ||
		(Info.Port != 1080u) ) {
增量握手

xrtNetProxyHandshakeConfigInit函数

初始化握手配置;64 KiB 上限主要约束后续 HTTP CONNECT Header。

void xrtNetProxyHandshakeConfigInit(xnetproxyhandshakeconfig* pConfig)

参数

参数方向约束说明
pConfig输出非空接收配置
返回值
返回含义失败时状态
无已初始化—
错误
  • 无 — 初始化不失败

范例已编译验证

proxy_tour · 握手配置

	xrtNetProxyHandshakeConfigInit(&HsConfig);

xrtNetProxyHandshakeCreate函数

创建握手并立即生成首个协议报文;目标主机会被深拷贝。

xnetproxyhandshake* xrtNetProxyHandshakeCreate(const xnetproxyhandshakeconfig* pConfig)

参数

参数方向约束说明
pConfig输入非空且通过校验握手配置
返回值
返回含义失败时状态
非空握手对象,首个报文已就绪—
NULL创建失败xrt.net 域错误
错误
  • xrt.net / XNET_ERROR_PROXY_CONFIG(XERR_ARGUMENT / XERR_VALUE) — 配置字段非法或组合不支持
  • xrt.net / XNET_ERROR_PROXY_CREATE — 对象或内部缓冲分配失败(XERR_MEMORY 等)
  • xrt.net / XNET_ERROR_PROXY_LIMIT(XERR_RANGE) — 超出协议上限

范例已编译验证

proxy_tour · 创建握手

	pHandshake = xrtNetProxyHandshakeCreate(&HsConfig);

xrtNetProxyHandshakeDestroy函数

销毁握手,并清零尚未发送的认证报文和内部目标信息。

void xrtNetProxyHandshakeDestroy(xnetproxyhandshake* pHandshake)

参数

参数方向约束说明
pHandshake输入允许空空 = 空操作
返回值
返回含义失败时状态
无已销毁—
错误
  • 无 — 释放不失败

范例已编译验证

proxy_tour · 销毁握手

	xrtNetProxyHandshakeDestroy(pHandshake);

xrtNetProxyHandshakeState函数

返回当前握手状态;空指针返回 ERROR。

xnetproxyhandshakestate xrtNetProxyHandshakeState(const xnetproxyhandshake* pHandshake)

参数

参数方向约束说明
pHandshake输入非空目标握手
返回值
返回含义失败时状态
XNET_PROXY_HANDSHAKE_WRITE有输出待发送—
XNET_PROXY_HANDSHAKE_READ等待代理回复—
XNET_PROXY_HANDSHAKE_READY隧道已建立—
XNET_PROXY_HANDSHAKE_ERROR失败(含空句柄)见 HandshakeError
错误
  • 无 — 状态查询不设置错误;空句柄返回 ERROR

范例已编译验证

proxy_tour · 握手状态

		(xrtNetProxyHandshakeState(pHandshake) !=
			XNET_PROXY_HANDSHAKE_WRITE) ||

xrtNetProxyHandshakeStep函数

处理输入链中的完整协议前缀;只消费代理回复,成功后的应用数据保持原位。WRITE 状态必须先发送并确认全部输出,READ 状态才会继续解析输入。

xnetproxyhandshakestate xrtNetProxyHandshakeStep(xnetproxyhandshake* pHandshake, xnetbuf* pInput)

参数

参数方向约束说明
pHandshake输入/输出非空目标握手
pInput输入/输出非空输入链
返回值
返回含义失败时状态
XNET_PROXY_HANDSHAKE_WRITE有输出待发送—
XNET_PROXY_HANDSHAKE_READ等待代理回复—
XNET_PROXY_HANDSHAKE_READY隧道已建立—
XNET_PROXY_HANDSHAKE_ERROR失败—
错误
  • XERR_ARGUMENT — 指针为空或参数非法
  • xrt.net / XNET_ERROR_PROXY_PROTOCOL — 协议状态非法、回复不完整或回复码表示失败
  • xrt.net / XNET_ERROR_PROXY_LIMIT(XERR_RANGE) — 超出协议上限

范例已编译验证

proxy_socks5 · 推进握手

		(xrtNetProxyHandshakeStep(pHandshake, &Input) !=
		 XNET_PROXY_HANDSHAKE_WRITE) ||

xrtNetProxyHandshakeOutput函数

借用当前待发送的首段连续输出;失败时把非空输出规范化为空 Span。

bool xrtNetProxyHandshakeOutput(const xnetproxyhandshake* pHandshake, xnetspan* pOutput)

参数

参数方向约束说明
pHandshake输入非空目标握手
pOutput输出非空接收输出 Span
返回值
返回含义失败时状态
trueSpan 已写出(可为空)—
false参数或状态非法XERR_ARGUMENT / XERR_STATE
错误
  • XERR_ARGUMENT — 指针为空或参数非法
  • XERR_STATE — 非 WRITE 状态请求输出

范例已编译验证

proxy_tour · 待发送输出

		!xrtNetProxyHandshakeOutput(pHandshake, &Output) ||

xrtNetProxyHandshakeSent函数

确认已经发送的输出前缀;支持 Socket 部分写入。

size_t xrtNetProxyHandshakeSent(xnetproxyhandshake* pHandshake, size_t iSize)

参数

参数方向约束说明
pHandshake输入/输出非空、WRITE 状态目标握手
iSize输入<= 待发送量本次已发送字节数
返回值
返回含义失败时状态
>= 0剩余待发送字节数,0 = 全部确认—
0参数或状态非法XERR_ARGUMENT / XERR_STATE
错误
  • XERR_ARGUMENT — 指针为空或参数非法
  • XERR_STATE — 非 WRITE 状态或确认量超出待发送量

范例已编译验证

proxy_tour · 确认发送

	(void)xrtNetProxyHandshakeSent(pHandshake, Output.Size);

xrtNetProxyHandshakeBound函数

READY 后复制可用的绑定端点;HTTP CONNECT 没有该信息并返回 NOT_FOUND。

bool xrtNetProxyHandshakeBound(const xnetproxyhandshake* pHandshake, xnetproxyendpoint* pEndpoint)

参数

参数方向约束说明
pHandshake输入非空、已 READY目标握手
pEndpoint输出非空接收端点
返回值
返回含义失败时状态
true端点已复制—
false无绑定信息或状态非法XERR_NOT_FOUND 等
错误
  • XERR_ARGUMENT — 指针为空
  • XERR_STATE — 尚未 READY
  • XERR_NOT_FOUND(xrt.net / PROXY_PROTOCOL) — HTTP CONNECT 无绑定端点

范例已编译验证

proxy_tour · 绑定端点

	if ( xrtNetProxyHandshakeBound(pHandshake, &Endpoint) ||
		xrtNetProxyHandshakeCode(pHandshake, &iCode) ||
		(xrtNetProxyHandshakeError(pHandshake) != NULL) ) {

xrtNetProxyHandshakeError函数

返回协议失败时捕获的不可变错误;对象所有权仍属于握手。

const xerror* xrtNetProxyHandshakeError(const xnetproxyhandshake* pHandshake)

参数

参数方向约束说明
pHandshake输入非空目标握手
返回值
返回含义失败时状态
非空错误借用(存活到销毁或下一次 Step)—
NULL尚无错误不设错
错误
  • 无错误 — 非 ERROR 状态返回 NULL 且不设置错误

范例已编译验证

proxy_tour · 握手错误

		(xrtNetProxyHandshakeError(pHandshake) != NULL) ) {

xrtNetProxyHandshakeCode函数

复制 SOCKS5 线路回复码或 HTTP 状态码;尚未收到回复时返回 false。

bool xrtNetProxyHandshakeCode(const xnetproxyhandshake* pHandshake, uint32* pCode)

参数

参数方向约束说明
pHandshake输入非空目标握手
pCode输出非空接收回复码
返回值
返回含义失败时状态
true回复码已复制—
false尚未收到回复不设错
错误
  • 尚未收到回复返回 false 且不设置错误;句柄或输出为空 XERR_ARGUMENT

范例已编译验证

proxy_tour · 线路回复码

		xrtNetProxyHandshakeCode(pHandshake, &iCode) ||
托管代理拨号

xrtNetProxyDialConfigInit函数

初始化 TCP 拨号、64 KiB 协议上限和 30 秒全过程超时。

void xrtNetProxyDialConfigInit(xnetproxydialconfig* pConfig)

参数

参数方向约束说明
pConfig输出非空接收配置
返回值
返回含义失败时状态
无已初始化—
错误
  • 无 — 初始化不失败

范例已编译验证

proxy_tour · 拨号配置

	xrtNetProxyDialConfigInit(&DialConfig);

xrtNetProxyDial函数

连接代理端点并完成目标 CONNECT;成功 Stream 引用转移给完成回调。非 Worker 提交者可能与完成回调并发,不能依赖返回值已经完成赋值。

xnetproxydial* xrtNetProxyDial(xnetengine* pEngine, xnetresolver* pResolver, const xnetproxy* pProxy, cstr sTargetHost, uint16 iTargetPort, const xnetproxydialconfig* pConfig, const xnetstreamevents* pStreamEvents, ptr pStreamData, xnetproxydialproc pDone, ptr pDoneData)

参数

参数方向约束说明
pEngine输入非空网络 Engine
pResolver输入允许空名称解析器
pProxy输入非空代理对象
sTargetHost输入非空、零结尾目标主机
iTargetPort输入—目标端口
pConfig输入允许空空 = 默认配置
pStreamEvents输入允许空Stream 事件表
pStreamData输入任意值Stream 数据
pDone输入非空完成回调
pDoneData输入任意值回调数据
返回值
返回含义失败时状态
非空拨号对象(引用 1)—
NULL提交失败xrt.net 域错误
错误
  • XERR_ARGUMENT — 指针为空或参数非法
  • xrt.net / XNET_ERROR_PROXY_CONFIG(XERR_ARGUMENT / XERR_VALUE) — 配置字段非法或组合不支持
  • xrt.net / XNET_ERROR_PROXY_CREATE — 对象或内部缓冲分配失败(XERR_MEMORY 等)
  • xrt.net / XNET_ERROR_PROXY_CONNECT — 连接提交失败
  • xrt.net / XNET_ERROR_PROXY_UNSUPPORTED(XERR_UNSUPPORTED) — 配置的协议或地址族不支持

范例已编译验证

proxy_dial · 发起拨号

	pDial = xrtNetProxyDial(
		pEngine,
		pResolver,
		pProxy,
		argv[3],
		iTargetPort,
		&DialConfig,
		&StreamEvents,
		&Example,
		exampleProxyDialDone,
		&Example

xrtNetProxyDialRef函数

增加 Proxy Dial 引用并返回原指针。

xnetproxydial* xrtNetProxyDialRef(xnetproxydial* pDial)

参数

参数方向约束说明
pDial输入非空目标拨号
返回值
返回含义失败时状态
非空原指针,引用 +1—
NULL参数非法XERR_ARGUMENT
错误
  • XERR_ARGUMENT — 指针为空或参数非法

范例已编译验证

proxy_tour · 共享引用

	pDialRef = xrtNetProxyDialRef(pDial);

xrtNetProxyDialDestroy函数

释放 Proxy Dial 引用;空指针视为空操作。

void xrtNetProxyDialDestroy(xnetproxydial* pDial)

参数

参数方向约束说明
pDial输入允许空空 = 空操作
返回值
返回含义失败时状态
无引用 -1—
错误
  • 无 — 释放不失败

范例已编译验证

proxy_tour · 释放引用

	xrtNetProxyDialDestroy(pDialRef);

xrtNetProxyDialCancel函数

协作取消名称解析、TCP 连接或代理握手;首个取消请求获胜,已终态对象返回 false 且不设错。

bool xrtNetProxyDialCancel(xnetproxydial* pDial)

参数

参数方向约束说明
pDial输入非空目标拨号
返回值
返回含义失败时状态
true取消请求已被受理—
false已连接、已失败、已取消或并发取消已被受理不设错误
错误
  • 已终态或并发取消已被受理返回 false 且不设置错误;句柄为空 XERR_ARGUMENT

范例已编译验证

proxy_dial · 协作取消

		(void)xrtNetProxyDialCancel(pDial);

xrtNetProxyDialState函数

返回当前拨号阶段或不可变终态。

xnetproxydialstate xrtNetProxyDialState(const xnetproxydial* pDial)

参数

参数方向约束说明
pDial输入非空目标拨号
返回值
返回含义失败时状态
XNET_PROXY_DIAL_RESOLVING / CONNECTING / HANDSHAKE进行中阶段—
XNET_PROXY_DIAL_CONNECTED / FAILED / CANCELLED不可变终态—
错误
  • 无 — 原子状态查询不设置错误;空句柄返回 RESOLVING

范例已编译验证

proxy_tour · 拨号状态

		xnetproxydialstate State = xrtNetProxyDialState(pDial);

xrtNetProxyDialError函数

失败或取消后借用完整错误原因链。

const xerror* xrtNetProxyDialError(const xnetproxydial* pDial)

参数

参数方向约束说明
pDial输入非空目标拨号
返回值
返回含义失败时状态
非空错误借用(存活到对象销毁)—
NULL进行中或已连接不设错
错误
  • 无错误 — 无失败时返回 NULL 且不设置错误

范例已编译验证

proxy_tour · 拨号错误

			(xrtNetProxyDialError(pDial) == NULL) ||

xrtNetProxyDialStats函数

复制代理阶段和底层 TCP 地址竞速统计。

bool xrtNetProxyDialStats(const xnetproxydial* pDial, xnetproxydialstats* pStats)

参数

参数方向约束说明
pDial输入非空目标拨号
pStats输出非空接收统计快照
返回值
返回含义失败时状态
true快照已复制—
false参数非法XERR_ARGUMENT
错误
  • XERR_ARGUMENT — 指针为空或参数非法

范例已编译验证

proxy_tour · 拨号统计

			!xrtNetProxyDialStats(pDial, &DialStats) ||

xnetstreamevents类型

Stream 回调全部在所属 Worker 上串行执行。

字段类型语义
Openvoid (*Open)(xnetstream* pStream, ptr pData)流开放
Readvoid (*Read)(xnetstream* pStream, xnetbuf* pBuffer, ptr pData)收到数据缓冲
Endvoid (*End)(xnetstream* pStream, ptr pData)对端写关闭
HighWatervoid (*HighWater)(xnetstream* pStream, size_t iQueued, ptr pData)发送占用越过高水位
LowWatervoid (*LowWater)(xnetstream* pStream, size_t iQueued, ptr pData)发送占用落回低水位
Drainvoid (*Drain)(xnetstream* pStream, ptr pData)发送队列排空
Closevoid (*Close)(xnetstream* pStream, xnetresult Result, const xerror* pError, ptr pData)流关闭(含终态结果与错误)
typedef struct xnetstreamevents { void(*Open)(xnetstream* pStream, ptr pData); void (*Read)(xnetstream* pStream, xnetbuf* pBuffer, ptr pData); void (*End)(xnetstream* pStream, ptr pData); void (*HighWater)(xnetstream* pStream, size_t iQueued, ptr pData); void (*LowWater)(xnetstream* pStream, size_t iQueued, ptr pData); void (*Drain)(xnetstream* pStream, ptr pData); void (*Close)(xnetstream* pStream, xnetresult Result, const xerror* pError, ptr pData)

常量索引常量

以下常量在本页所属头文件中定义,语义见公共头注释。

常量说明
XNET_PROXY_AUTH_AUTO见头文件注释
XNET_PROXY_AUTH_NONE见头文件注释
XNET_PROXY_AUTH_OPTIONAL见头文件注释
XNET_PROXY_AUTH_REQUIRED见头文件注释
XNET_PROXY_DIAL_CANCELLED见头文件注释
XNET_PROXY_DIAL_CONNECTED见头文件注释
XNET_PROXY_DIAL_CONNECTING见头文件注释
XNET_PROXY_DIAL_FAILED见头文件注释
XNET_PROXY_DIAL_HANDSHAKE见头文件注释
XNET_PROXY_DIAL_RESOLVING见头文件注释
XNET_PROXY_HANDSHAKE_ERROR见头文件注释
XNET_PROXY_HANDSHAKE_READ见头文件注释
XNET_PROXY_HANDSHAKE_READY见头文件注释
XNET_PROXY_HANDSHAKE_WRITE见头文件注释
XNET_PROXY_HTTP_CONNECT见头文件注释
XNET_PROXY_SOCKS5见头文件注释
XNET_SOCKS5_ADDRESS_UNSUPPORTED见头文件注释
XNET_SOCKS5_COMMAND_UNSUPPORTED见头文件注释
XNET_SOCKS5_CONNECTION_REFUSED见头文件注释
XNET_SOCKS5_GENERAL_FAILURE见头文件注释
XNET_SOCKS5_HOST_UNREACHABLE见头文件注释
XNET_SOCKS5_NETWORK_UNREACHABLE见头文件注释
XNET_SOCKS5_RULESET_DENIED见头文件注释
XNET_SOCKS5_SUCCEEDED见头文件注释
XNET_SOCKS5_TTL_EXPIRED见头文件注释