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

第11篇_Server 07|用通信猫、curl 和在线变量完成真机验收

适合谁收藏

  • 正在实现 PLC 端 HTTP Server 的工程师。
  • 正在排查监听、多连接、请求边界或响应发送问题的人。
  • 需要建立 Server 真机验收清单的读者。

本篇位置服务器篇,第 7/7 篇;主系列第 11/28 篇。

现场问题

同一套 Server,用通信猫、curl 和浏览器访问时,Header、连接复用和默认行为并不一样。只用一个工具跑一次,很难覆盖真实边界。

发布级验证需要一张矩阵:外部端发了什么,PLC 收到了什么,状态机走到哪里,返回了什么,计数器为什么变化。

先给结论

Server 通过标准是双向可解释:外部工具得到合法响应,PLC 在线量同时留下监听句柄、槽位、请求路径、响应报文、错误码和计数证据。

读图重点

这张图只压缩本篇的判断路径。读图时先找“通信猫”对应的输入边界,再沿着“工程内部状态”检查状态怎样推进,最后用“解释外部现象对应哪个阶段”确认输出是否已经形成验收证据。

把对象和边界分开

对象或阶段工程职责现场观察点
通信猫可控原始请求和分片验证半包、坏 Header 和自定义 Body
curl可见请求响应细节验证状态码、Header、关闭行为
浏览器真实客户端默认行为验证 Host、复用和容错边界
在线变量工程内部状态解释外部现象对应哪个阶段

从协议约束到代码职责

协议约束

Server 通过标准是双向可解释:外部工具得到合法响应,PLC 在线量同时留下监听句柄、槽位、请求路径、响应报文、错误码和计数证据。 这条结论先限定消息什么时候成立,再限定哪个角色可以消费结果。若绕过协议边界直接驱动业务,半包、超时、重复执行和连接残留就会进入应用层。

工程抽象

真机测试全局量保留 Server/Client 的目标地址、端口、运行命令和观察结果。文章公开的是变量职责和验收方法,不暴露本机内部路径或过程证据文件名。

稳定性不能用“连续运行没报错”替代。至少要确认接入计数、请求计数、响应计数和错误计数符合实际请求次数,并且断开后的槽位能够回收。

  • 通信猫:工程职责是“可控原始请求和分片”。它不能只停留在命名层面,运行时必须能通过“验证半包、坏 Header 和自定义 Body”观察到输入、状态或结果;否则这一层即使有代码,也没有形成可验证边界。
  • curl:工程职责是“可见请求响应细节”。它不能只停留在命名层面,运行时必须能通过“验证状态码、Header、关闭行为”观察到输入、状态或结果;否则这一层即使有代码,也没有形成可验证边界。
  • 浏览器:工程职责是“真实客户端默认行为”。它不能只停留在命名层面,运行时必须能通过“验证 Host、复用和容错边界”观察到输入、状态或结果;否则这一层即使有代码,也没有形成可验证边界。
  • 在线变量:工程职责是“工程内部状态”。它不能只停留在命名层面,运行时必须能通过“解释外部现象对应哪个阶段”观察到输入、状态或结果;否则这一层即使有代码,也没有形成可验证边界。

程序单元

本篇主证据来自GVL_HttpRealTest.st中以VAR_GLOBAL为定位点的连续源码。这里不是为了展示语法,而是把协议约束落到确定程序单元:输入先进入结构体或缓冲区,状态机只在本周期处理可确认的部分,长度和结束条件决定能否前进,错误码与指标负责把失败原因带出对象边界。这样一来,“真机通过必须同时看外部端和 PLC。”可以在代码、在线变量和外部报文之间逐项对照,而不是依赖经验猜测。

本篇核心源码片段

下面两段代码来自同一个真实文件GVL_HttpRealTest.st,以VAR_GLOBAL为中心连续截取,没有改写变量、删除分支或用伪代码替代。第一段用于确认入口与前置条件,第二段用于确认状态、边界和输出。核对时重点看“可控原始请求和分片”怎样进入对象,以及“解释外部现象对应哪个阶段”怎样证明本次处理已经结束。若两段之间的连续关系无法解释“计数器必须能解释每一次测试。”,就不能把局部代码截图当成实现证据。

片段一:入口、声明与前置条件

VAR_GLOBAL bResetResults : BOOL := FALSE; // 一次性复位真机联调结果和命令位。 udiNowMs : UDINT := 0; // 示例任务毫秒时钟,按扫描周期累加。 bServerEnable : BOOL := TRUE; // Server 使能,下载后默认监听,便于通信猫直接访问 PLC。 bServerCloseConnection : BOOL := TRUE; // Server 响应后主动关闭连接,匹配通信猫短连接调试习惯。 sServerBindIP : STRING := '0.0.0.0'; // Server 绑定 IP,0.0.0.0 表示任意网卡。 uiServerPort : UINT := GVL_Http.cnDefaultServerPort; // Server 监听端口,默认 18088,匹配通信猫访问 PLC 的 URL。 uiServerResponseStatusCode : UINT := 200; // Server 自定义响应状态码,内置路由默认 200。 sServerResponseBody : STRING(GVL_Http.cnMaxBodySize) := 'hello from PLC Server';// Server 自定义响应 body,非空时未知路径也返回 200。 sServerResponseContentType : STRING(96) := GVL_Http.cnDefaultContentType; // Server 自定义响应 Content-Type。 sServerAdditionalHeader : STRING(GVL_Http.cnMaxHeaderSize) := ''; // Server 自定义响应 Header 行。 bServerListening : BOOL := FALSE; // Server 已获得监听句柄。 bServerRunning : BOOL := FALSE; // Server 正在运行。 bServerBusy : BOOL := FALSE; // Server 正在初始化或等待监听。 bServerError : BOOL := FALSE; // Server 错误锁存。 diServerErrorID : DINT := 0; // Server 错误诊断码。 sServerDiagMsg : STRING(255) := ''; // Server 诊断文本。 eServerState : E_HttpServerState := E_HttpServerState.iDisabled; // Server 状态机。 eServerLastError : E_HttpError := E_HttpError.iNoError; // Server 最近 HTTP 错误。 eServerLastNbsError : NBS.ERROR; // Server 最近 NBS 错误。 uiServerActiveConnections : UINT := 0; // Server 当前活跃连接数。 uiServerLastRequestSlot : UINT := 0; // Server 最近请求槽位。 uiServerLastErrorSlot : UINT := 0; // Server 最近错误槽位。 sServerLastTarget : STRING(GVL_Http.cnMaxTargetLen) := ''; // Server 最近请求路径。 sServerLastBody : STRING(GVL_Http.cnMaxBodySize) := ''; // Server 最近请求 body。 sServerRxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Server 最近一次接收报文。 sServerTxMessage : STRING(GVL_Http.cnMaxMessageSize) := ''; // Server 最近一次发送报文。 udiServerAcceptedCount : UDINT := 0; // Server 累计接入连接数。

这一段先回答对象在什么输入和状态下开始工作。阅读时要核对变量的初值、长度上限和启动条件,不能只看某个布尔量是否变成 TRUE。

片段二:状态推进、边界与输出

udiServerRequestCount : UDINT := 0; // Server 累计请求数。 udiServerResponseCount : UDINT := 0; // Server 累计响应数。 udiServerProtocolErrorCount : UDINT := 0; // Server 累计协议错误数。 hServerListenHandle : NBS.CAA.HANDLE; // Server 监听句柄快照。 bServerPassLatched : BOOL := FALSE; // Server 外部 client 联调通过锁存。 bClientEnable : BOOL := TRUE; // Client 使能,下载后可直接向通信猫发起 HTTP 请求。 bClientSend : BOOL := TRUE; // Client 下载后默认发送一次,PLC_PRG 读取后自复位。 bClientAbort : BOOL := FALSE; // Client 中止当前连接命令。 bClientCloseConnection : BOOL := TRUE; // Client 每次响应后主动关闭连接,优先保证通信猫短连接调试稳定。 udiClientTimeoutUs : UDINT := 10000000; // Client 请求超时,单位 [us],通信猫联调默认 10 s。 sClientURL : STRING(1024) := ''; // Client URL 留空时使用下方 IP/Host/Path 分离引脚。 sClientServerIP : STRING := '192.168.20.2'; // Client 远端 HTTP Server IP。 uiClientPort : UINT := 6971; // Client 远端 HTTP Server 端口,匹配通信猫 HTTP 服务端口。 sClientHost : STRING(GVL_Http.cnMaxHostLen) := '192.168.20.2:6971'; // Client Host 字段,匹配通信猫监听端口。 sClientPath : STRING(GVL_Http.cnMaxTargetLen) := '/api/tongxinmao'; // Client 请求路径,匹配通信猫 URL。 eClientMethod : E_HttpMethod := E_HttpMethod.iPost; // Client 请求方法,默认 POST 便于发送正文。 sClientBody : STRING(GVL_Http.cnMaxBodySize) := 'hello from PLC'; // Client 请求 body,通信猫接收区可直接看到。 sClientContentType : STRING(96) := 'text/plain'; // Client Content-Type,匹配通信猫普通文本联调。 sClientAdditionalHeader : STRING(GVL_Http.cnMaxHeaderSize) := ''; // Client 自定义 Header 行。 bClientTcpConnected : BOOL := FALSE; // Client TCP 连接快照。 bClientBusy : BOOL := FALSE; // Client 正在执行事务。 bClientDone : BOOL := FALSE; // Client 本次事务完成。 bClientError : BOOL := FALSE; // Client 错误锁存。 diClientErrorID : DINT := 0; // Client 错误诊断码。 sClientDiagMsg : STRING(255) := ''; // Client 诊断文本。 eClientState : E_HttpClientState := E_HttpClientState.iDisabled; // Client 状态机。 eClientLastError : E_HttpError := E_HttpError.iNoError; // Client 最近 HTTP 错误。 eClientLastNbsError : NBS.ERROR; // Client 最近 NBS 错误。

第二段继续展示同一连续源码范围。把它与第一段合起来,才能判断输入怎样被锁存、状态何时推进、边界何时满足,以及错误出口是否保留了足够诊断信息。

验证路径

场景操作通过口径
正常 GET访问 /api/ping200 和 pong
正常 POST访问 /api/echoBody 原样返回
协议错误缺 Host、坏长度、坏 chunk明确错误响应与计数
连续事务多轮短连接和顺序长连接无句柄泄漏和跨请求污染

场景 1:正常 GET

在通信猫里发送完整的GET /api/ping HTTP/1.1,并故意把发送动作拆成两段。PLC 侧应先看到接收长度增长,再在报文边界完整后进入路由;curl 收到的必须是带Content-Length200/pong。如果通信猫只发半包就触发响应,说明接收边界判断错误;如果 curl 成功而 accepted 或 response 计数不闭合,也不能算通过。

场景 2:正常 POST

用 curl 的--data发送一段可辨认的文本,抓取请求和响应的 Header 与 Body。验收点不是只有返回 200,而是响应 Body 与输入逐字相同、Content-Length与实际字节数相等,同时在线变量里的请求长度和响应长度可解释。若 echo 结果正确但长度计数未归零或下一轮仍带着旧 Body,问题在事务收口而不在路由。

场景 3:协议错误

分别构造缺失Host、伪造长度和不完整 chunk 的原始请求。预期是 Server 给出可区分的协议错误或主动关闭连接,而不是卡在 Busy、也不是把坏数据交给业务回调。每次失败后核对 error 计数只增加一次、槽位释放、下一笔正常 ping 仍能完成;这样才能证明错误路径没有污染后续连接。

场景 4:连续事务

先连续建立多次短连接,再在同一连接上顺序执行两笔请求,期间记录 accepted、closed、activeSlots 与请求序号。通过条件是每笔响应只对应本次路径,短连接结束后槽位归零,长连接的第二笔不会复用第一笔 Body 或状态码。若运行几轮后 activeSlots 单调上升,必须先定位回收条件,不能把它解释成客户端偶发。

常见误判

  • 只用一种工具发一次正常 GET,就把 Server 标记为真机验收通过。
  • 外部工具得到响应,却不核对 accepted、request、response 和 error 计数。
  • 测试结束不确认槽位回收,连续运行后才暴露连接资源泄漏。

这些误判的共同点,是拿一个局部现象替代完整事务。定位时必须回到本篇的输入、状态、边界和输出四个坐标,并用相同输入完成回归。

这一篇你最该记住

  • 真机通过必须同时看外部端和 PLC。
  • 不同工具是互补证据,不是互相替代。
  • 计数器必须能解释每一次测试。

系列导航

  • 系列:CodeSys HTTP 系列教程,第 11/28 篇。
  • 阶段:服务器篇,职责线位置 7/7。
  • 上一篇:第10篇
  • 下一篇:第12篇
  • 发布顺序:基础认知 -> Server -> Client -> 完整源码加更 -> 综合收束。
http://www.jsqmd.com/news/1364963/

相关文章:

  • VisionMaster 断续划痕检测全流程算子实操详解
  • BetterGI原神AI工具:从繁琐操作到智能游戏的终极解决方案
  • 微流控血脑屏障芯片技术解析与应用
  • Spring IOC容器启动流程与Bean生命周期详解
  • 一级减速器CAD图纸设计规范与核心要点解析
  • Android框架开发核心技术与实践指南
  • Python音频处理实战:基于pydub实现音频剪辑、混合与自动化
  • HCIA学习笔记(六):IP编址基础概念
  • 从零配置OGRE 3D引擎:C++图形开发入门与旋转立方体实战
  • 0391-Raylib-按钮动画和声音
  • 3分钟快速修复洛雪音乐:六音音源修复版完全指南
  • OpenClaw容器化部署实战:Docker与CUDA环境配置指南
  • 在VS2010中从零实现FFT算法:原理、代码与性能优化实战
  • XUnity.AutoTranslator:Unity游戏一键翻译的终极解决方案
  • 信息系统项目管理师备考全攻略:从教材精读到论文实战
  • Python实战:基于规则与NER的军事战报信息抽取与结构化处理
  • AI文章转PPT视频:本地部署与自动化流程全解析
  • Python高级特性实战:提升代码效率与性能
  • SSM+Vue英语学习网站开发全解析
  • Palantir Ontology 如何重塑半导体晶圆厂:从数据孤岛到业务操作系统
  • 终极飞书文档批量导出工具:告别手动下载,25分钟完成700+文档迁移
  • 2026年8月市面上佛山广告灯箱制造厂家哪家靠谱测评,超薄灯箱、拉布灯箱及异形定制厂家分析 - 海棠依旧大
  • 为什么都说网站建设属于软件开发其实这是一项复杂的系统工程的真相
  • 2026独立站搭建平台有哪些,跨境卖家建站工具选择指南
  • Houdini Engine for Unreal V2 安装配置与核心工作流程全解析
  • UE5 GameInstance核心职责与架构设计:从新手困惑到最佳实践
  • 2026年7月广州市越秀区二手房价格深度分析报告
  • Office安装神器,流批了
  • 我把 10 万行祖传代码喂给了 AI:用 RAG 搭建“代码考古“助手,新人 1 天看懂老项目
  • Python a0-baas-sdk包解析与BaaS开发实战