Code1 证明字节写入不替设备动作和实物结果作保证。摘要我沿 Microi吾码AI 当前 V8.Tcp 实现从接口引擎的可信设备目标、Bytes/Hex/Text 编码追到一次性 TCP 连接、有界收发、协议回执和业务终态。结论Code1 只证明字节已经写入 TCP 连接要证明设备接受、机械动作和小票出纸必须继续读取协议回执、设备状态或现场证据。本文绑定当前源码行号、SHA-256 与 8/8 聚焦测试。✦① 后端说成功小票机为什么一动不动设备接入最常见的误判是把 V8.Tcp.Send 返回 Code1 直接翻译成“小票已打印”。实际上当前返回的数据只有 BytesSent 和 RemoteEndPoint它证明一次 TCP 连接完成了字节写入却不知道打印机是否解析了 ESC/POS 命令、纸仓是否缺纸、刀头是否卡住更不知道顾客手里是否真的拿到小票。网络写入、协议接受、设备动作和实物结果是四个不同的成功边界。把它们混成一个绿色对勾超时重试时就可能连续出两张票。V8.Tcp 负责有界 I/O协议与业务完成语义仍要由接口引擎编排。先给结论Code1 到网络为止设备是否执行要靠设备协议或更高层证据继续证明。✦② V8.Tcp 适合什么不适合什么V8.Tcp 是 Microi吾码AI 后端的一次性原始字节客户端适合 RAW/JetDirect 9100 小票机、串口服务器、PLC 或私有 TCP 协议。它公开同步和异步的发送、收发方法每次调用建立连接、执行有界 I/O 后关闭不向 V8 暴露持久 Socket。HTTP/HTTPS 接口应使用 V8.Http不要用 TCP 重写 HTTP。RabbitMQ/MQTT 使用 V8.MQ 或 MQTT 引擎不要混用协议层。长连接、TLS、TCP 服务端监听或自定义连接池应设计独立网关/Worker。设备选择、票据模板、幂等、审计和状态机继续放在接口引擎与业务表。平台边界V8.Tcp 是安全受限的原子 I/O 能力不是一套设备管理系统。✦③ 先把目标锁死再谈发什么字节TCP 不经过 V8.Http 的 SSRF 防护。如果把 Host、Port 从 V8.Param 原样传入普通调用者就获得了扫描后端可达内网的能力。生产实现应从 SaaS 可信配置或受行权限保护的设备表读取目标再按当前租户、门店和用户权限命中精确白名单。// device 必须来自可信配置/受权限保护的设备表 var device V8.FormEngine.GetFormData(device_terminal, { _Where: [[Code, , V8.Param.deviceCode], [Enabled, , 1]], _SelectFields: [Host, Port] }); if (device.Code ! 1) return { Code: 0, Msg: 设备不可用 }; // 禁止 Host: V8.Param.Host / Port: V8.Param.Port var host device.Data.Host; var port device.Data.Port;安全底线调用者选择的是已授权设备编号不是任意 IP 与端口。✦④ 一个完整帧只能选择一种载荷当前实现允许 Bytes、ByteBase64、Hex、Text 四种表达但一次调用必须且只能提供一种。控制命令、正文、换行、切纸和校验码要先组成完整协议帧再打开一次连接发送。中文小票可以使用 Text Encoding: gb18030混合控制字节时更适合提前编码成 Bytes、Hex 或 Base64。var result V8.Tcp.Send({ Host: host, Port: port, Bytes: [27, 64, 77, 105, 99, 114, 111, 105, 10, 29, 86, 0], ConnectTimeout: 5, SendTimeout: 5 }); if (result.Code ! 1) return result; // 此处只能记录 tcp_written不能记录 printed。源码把连接、发送和接收超时限制在 1120 秒单次发送硬上限 4 MiB默认最多读取 64 KiB绝对接收上限 1 MiB。硬上限是防止 V8 脚本把一次设备调用变成无限内存或无限等待。✦⑤ 需要证明设备接受就显式读取协议回执每一层都应拥有独立状态与证据。设备协议定义 ACK、状态字或响应帧时应使用 SendAndReceive。返回值包含 RawBytes、Hex、ByteBase64、BytesReceived、ReceiveEndReason 和 Truncated接口引擎必须按协议长度、校验码和状态位解析不能因为收到任意一个字节就宣布成功。var reply V8.Tcp.SendAndReceive({ Host: host, Port: port, Hex: 01 03 00 00 00 02 C4 0B, ReceiveTimeout: 3, MaxReceiveBytes: 4096 }); if (reply.Code ! 1) return reply; if (reply.Data.Truncated || reply.Data.ReceiveEndReason Timeout) return { Code: 0, Msg: 响应不完整进入结果未知态 }; // 下一步按设备协议校验长度、CRC 和状态码。当前接收语义很细零字节直到超时会失败已经收到部分数据后超时会返回成功和已有字节并标记 ReceiveEndReasonTimeout达到上限则标记 MaxReceiveBytes。因此 Code1 也不自动等于完整响应。✦⑥ 超时后最危险的动作是立刻再打一遍打印、开闸、继电器动作都不是天然幂等。发送超时可能发生在字节尚未到达也可能发生在设备已经执行但响应丢失。把这两种情况都当失败并自动重试会制造重复出纸或重复动作。结果未知必须成为正式状态不能被代码顺手折叠成失败。为每条设备命令生成稳定 CommandId并在数据库记录待发送。用 outbox、Job 或 MQ 驱动后台发送不把长 I/O 留在前端请求里。分别记录 tcp_written、device_acknowledged、action_confirmed、unknown。结果未知时优先查询设备状态、回读序号或人工确认再决定是否重试。日志只留设备标识、业务单号、字节数、耗时和错误分类不落票据秘密。✦⑦ 这次真正验证了哪些能力当前本地 V8.Tcp 聚焦测试 8/8 通过失败为 0。我在当前已编译的 Microi.Tests 程序集上执行 V8TcpTests8 项、8 项通过、0 项失败。测试不是只断言方法名而是真实启动本机 TCP listener经过 Jint 注入调用 V8.Tcp核对字节收发还覆盖 Hex、Base64、GB18030、歧义载荷拒绝、4 MiB 上限、部分超时和响应截断。这证明当前实现与回环测试合同一致。它没有连接真实打印机、PLC 或串口服务器也没有证明生产 Docker 到设备网段的路由、防火墙和白名单更不能替代现场出纸验收。验证边界回环测试证明原始字节 I/O真实设备协议、机械动作与实物结果必须单独验收。✦⑧ 结尾设备接入最值钱的是诚实的完成语义V8.Tcp 把一次性连接、四类载荷、有界超时、有界接收和多种返回格式封装成了接口引擎可用的原子能力。平台底座解决的是“安全、可控地发和收”业务系统还要继续解决“发给谁、哪条命令、设备是否接受、动作是否完成、未知结果如何恢复”。下一次看到 Code1不要急着弹出“打印成功”。先问这是字节写入、协议确认、设备动作还是实物结果能证明到哪一层系统就只承诺到哪一层。这一个看似保守的原则往往能省掉最多的重复打印、错账和现场扯皮。本文验证范围当前源码 SHA、行号与 2026-08-23 的 8/8 本地聚焦测试成立未把回环测试冒充真实设备出纸。