首页 / 资讯中心 / 文章详情

S7通讯协议族详解:从ISO-on-TCP到TPKT/COTP报文与排障实战

S7通讯协议族详解:从ISO-on-TCP到TPKT/COTP报文与排障实战 ★ FEATURED ARTICLE
第三篇了。前两篇把S7通讯的连接方式和基础组态捋了一遍这一篇我想换个节奏把协议本身拎出来扒开看顺便把现场容易踩的坑集中聊一次。S7通讯这个名字听起来像是一个协议实际上是一族基于不同物理传输和不同应用规范的通讯方式总称真正在以太网上跑的那套往底层拆开至少叠着TPKT、COTP和S7 PDU三层结构。不理解这三层连接出问题基本靠换线重启理解了以后抓一次包就能定位七八成的问题。这篇适合搞PLC联网、上位机对接、数据采集的同行参考也适合刚接触S7通讯、想搞懂报文到底长什么样的朋友。1. 把S7通讯的家底翻出来1.1 所谓S7通讯到底是一族什么协议很多刚接触西门子PLC的人会有个错觉觉得S7通讯就是“用网线连接PLC然后读写数据”。这个理解不能说错但太笼统了。S7通讯这个叫法在西门子体系里其实覆盖了好几条不同的技术路线它们的历史渊源和底层实现差别很大。最早S7系列PLC之间通讯靠的是MPI多点接口后来在Profibus现场总线上也发展出对应的S7协议变体。到了以太网普及之后西门子又把S7协议移植到工业以太网上形成了基于TCP/IP的ISO-on-TCP承载方式这个才是我们现在最常说的“S7通讯”。后面S7-1200、S7-1500时代通讯能力进一步下沉到硬件和固件里才有了今天大家在项目里用的各种S7连接。理解这个演变过程有什么用呢最大的用处是排查问题时分清层。比如你用第三方库去连S7-1200底层走的是TCPTCP之上是TPKTTPKT之上是COTPCOTP之上才是S7自己的会话。某一步出了错表现完全不一样。如果只知道“S7通讯连不上”你根本不知道是IP地址配错了、防火墙拦截了、连接资源占满了还是S7握手没完成。把链路分层搞清楚排障是按层逐层看的而不是瞎试。1.2 六种通讯方式什么时候用哪种在S7的世界里通讯方式多得让人头疼。我按现场常见程度和适用场景整理了一张对照表实际选型时基本上看这张表就够了。通讯方式物理层典型场景使用频率ISO-on-TCP以太网PLC与上位机、第三方系统、SCADA通讯最高S7-1200/1500集成S7通讯以太网PLC之间读写数据、上云网关高FDL链路层以太网/Profibus老项目专用链路的点对点通讯低FMS报文规范Profibus早期PLC间的多主站通讯已淘汰MPIRS485调试下载少用于生产数据交互中PUT/GET服务以太网PLC之间的变量直接读写高老项目常见这里要特别说一句PUT/GET并不是一种独立协议它实际上是S7协议向上层提供的一组服务请求简化了PLC之间的变量读写。很多人把它和“S7通讯”当成两个东西项目文档里也经常写着“通过PUT/GET实现数据交换”这类描述严格说不太严谨。S7-300/400里用FB14/15封装的就是这套服务S7-1200/1500里开个“允许PUT/GET访问”开关本质也是开放这套服务。选型时不用纠结叫法只要知道同一家PLC之间通讯开PUT/GET最简单省事跨厂商系统对接用ISO-on-TCP加自己的协议栈最灵活。1.3 为什么实际项目里最常碰见ISO-on-TCP原因很简单TCP/IP栈到处都有。无论你用的是Windows、Linux还是嵌入式系统标准TCP/IP协议栈都是现成的S7通讯只不过是在普通的TCP端口102上定义了特定的应用层会话。端口102这个数字值得记住它就像S7通讯的门牌号抓包过滤和防火墙放行都靠它。另一方面现代工业以太网已经高度标准化绝大多数PLC都直接支持以太网接口不需要额外加通讯处理器。S7-1200自带网口S7-1500更是把通讯能力做进了系统里。相比老式MPI还要专门的编程电缆和地址分配ISO-on-TCP的部署成本低得多物理链路跑标准网线和交换机就行。工业现场选ISO-on-TCP还有工程上的便利它可以和西门子博途软件的在线诊断功能共用物理网络日常维护时不用来回切换连接方式。有个以前踩过的坑是有人把S7通讯跑在带VLAN隔离的管理型交换机里结果PLC之间的广播和多播被切掉了虽然看起来IP配置没问题但连接就是建立不起来。后来才发现S7通讯虽然承载在TCP上但有些诊断功能依赖第二层广播VLAN不能乱配。这类细节不看抓包基本找不到头绪。2. S7报文的三层结构与握手细节2.1 TPKT/COTPTCP之上的套娃用抓包软件第一次打开S7通讯流量时很多人会愣住明明是从TCP端口102收到的数据但Payload前面像是套了好几个头。那个感觉就像拆快递先看到运单TPKT再撕开气泡膜COTP最后才摸到货物本体S7 PDU。TPKT头是固定的4字节内容是版本号、保留字节和整个包的后续长度。它解决的是TCP字节流里的“选题分帧”问题让接收方能知道一条完整的应用消息在哪里结束。紧接着的COTP头是面向连接的传输协议控制头主要包含长度、PDU类型和TPDU序号。这里PDU类型通常是0xF0表示这是数据传输块后面跟着的S7 PDU才是真正的应用数据。别小看这两层“套娃”。我见过不止一个项目因为自己实现了TCP客户端却忘了在发送数据前先拼上TPKT和COTP头结果PLC端日志一直在提示协议错误连接反复被重置。如果你自己写底层S7通讯代码这两层格式拼错一个字节S7报文根本不会被解析。如果用现成的协议库那可以不用管但抓包理解问题时看到TPKT/COTP都长得一致也就够了。2.2 S7 PDU头与ROSCTR类型拆掉TPKT和COTP之后剩下的才是S7的芯。S7 PDU头是10字节里面最值得关注的就是第二个字节也就是ROSCTR字段。ROSCTR全称叫远程操作服务控制类型说人话就是这条消息是“请求”、“应答”还是“带载应答”。常见的ROSCTR值大概是这么几类Job类型表示主动请求比如读变量、写变量ACK表示纯确认应答不带数据ACK_Data表示应答且携带数据比如读取请求的返回结果还有一类UserData用途比较杂像“通讯测试”这类系统内部交换就属于它。每次抓包时用Wireshark过滤完端口102之后第二看这个字节基本就能判断通讯走到哪一步了。10字节头里还有几个长度字段比如参数长度和数据长度。这两个字段决定了报文后面参数区、数据区各占多少字节。现场调试时如果发现PLC返回“长度错误”之类的诊断信息先看这里是不是拼错了长度。用现成库时一般不会出这种问题但一旦出了至少知道去看哪里。2.3 一条完整读写请求长什么样以最常见的读取操作为例S7报文的结构可以拆成“请求头 功能码 变量定义”。功能码0x04是读请求0x05是写请求。变量定义里则包含了数据块号、存储区域代码、字节地址、访问长度等核心信息。比如你想读DB1里的DBD0双字在抓包里看到的参数区大致会包含功能码04变量数量1然后是一个变量定义子结构里面写着DB号1、区域代码DB通常是0x84、地址偏移0数据长度4字节。这些字段组合起来PLC才知道你要读的是DB1.DBD0而不是M区或者I区。存储区域代码值得记一下I区通常对应0x81Q区0x82M区0x83DB区0x84这几个最常见。我一直不建议手工去组装这种报文。原因一是细节太多一个保留位错了都要调半天二是不同固件版本对某些字段的容忍程度不一样你照着老手册拼的报文可能在新CPU上就是被拒绝。自己写代码做S7通讯没问题但最好是基于成熟的开源或商业协议栈去做二次封装同时配上抓包工具验证实际运行时的报文形态。写报文靠“跑起来验证”不靠背十六进制。2.4 握手连接建立的隐藏步骤很多人的概念里TCP连上就等于通讯建立成功。但在S7协议里TCP连接成功只是第一步后面还有几轮应用层握手全部完成才算真正能读写数据。先看TCP层客户端向PLC的102端口发起连接TCP三次握手完成后紧接着是COTP层的连接请求CR包PLC回复连接确认CC包。这一步一般发生在TCP握手之后立即完成。然后是S7层的通讯测试机制通常以一个UserData类型的短报文交互完成目的确认S7会话逻辑上处于激活状态。也就是说一次完整的S7连接建立至少经历“TCP握手 COTP握手 S7握手”三段过程。抓包时如果只看到TCP握手、没有看到后面的CR/CC说明你的通讯客户端还没走到S7协议那一步问题大概率出在协议实现上而不是网络不通。经常有这样的现场现象用ping测PLC通用TCP工具测端口102也能连上但业务层程序就是报超时。原因往往就是COTP或S7握手没通过。这时候不要怀疑网络了应该去看PLC侧连接资源是否被占满、访问权限是否开启、固件里的通讯设置是否限制了连接数。3. 用一组完整配置把通讯跑通3.1 组态里最容易被忽略的三个开关要用S7通讯真正读到一个变量很多人卡住的不是程序写不对而是PLC组态里几个不起眼的开关没开。我每次做联调第一件事都是先把这几个地方检查一遍。第一内部变量访问开关。S7-1200/1500里在设备组态的“防护与安全”或“操作与监视”里有“允许来自远程对象的PUT/GET通讯访问”这类选项不勾上外部的S7客户端根本进不来。第二数据块访问优化方式。PLC编程时新建DB块会默认勾选“优化的块访问”一旦勾了外部用绝对地址和标准S7协议栈很难按偏移访问。通讯专用的数据块里我一般会取消优化访问改成标准访问方式才能稳定被外部读取。第三连接资源。S7-1200/1500连接资源数量有限组态时如果HMI、调试器、通讯客户端同时占连接新连接可能被拒绝。现场检查PLC诊断缓冲区能看到连接资源分配情况。这三个开关互相独立但常常一起出问题访问开关没开、DB块是优化模式、连接资源不够这三种故障表现都可以是“连接成功但读数据失败”。所以项目规范里最好把组态检查形成一份清单每次调试前逐项确认而不是等到数据读不出来才一个一个猜。3.2 PLC侧程序与数据块设计通讯跑通之后程序侧的可靠性设计就要跟上。S7-300/400时代最经典的做法是用FB14GET和FB15PUT在OB1或者循环OB里周期调用配合背景数据块来管理连接状态。到了S7-1200/1500作为服务器端被动方的时候基本不需要写通讯程序组态里开好访问开关就行作为客户端主动方去读写别的PLC就常用TSEND_C/TRCV_C这类开放式通讯指令自己管理连接ID和数据缓冲区。数据块设计这块有很多讲究。通讯数据块我会单独建一个而不是和业务逻辑数据混在一起。这个块里专门放需要对外交换的变量比如设备的运行状态字、报警字、控制字、当前工艺参数等。每一条对外变量都加上注释说明量纲、取值范围和更新周期。之所以单独建块是为了让通讯程序和数据逻辑解耦操作人员只看通讯块对应关系工艺人员只看业务块谁也不影响谁。另外一个可靠性的做法是建一个独立于主扫描周期的通讯刷新区。主程序把需要发送的数据写到一个中间镜像区通讯功能从镜像区取数发送收到的数据先放到接收镜像区主程序再根据有效性标志判断是否使用。这样做能避免通讯扫描周期和主程序扫描周期不同步导致的数据撕裂感。说白了就是即使通讯正常外部看到的数据也不一定和PLC真实状态完全同步中间加一道缓存至少外部读到的是一段完整、一致的数据。3.3 上位机侧发起请求与抓包验证上位机侧的实现就灵活多了。可以用现成的协议库也可以自己写低层TCP客户端。不管哪种方式第一步都是先建一条到PLC IP地址102端口的TCP连接然后按S7协议流程完成握手。个人经验是最初调试时先用一个已经成熟的Windows或Linux下的通讯测试工具跑通基本读写再切到自己写的代码这样能把协议问题和代码问题分开。跑通基本读写之后我强烈建议开一次Wireshark过滤条件直接填tcp.port 102然后把读写操作完整执行一遍。重点观察三类包建立连接阶段的CR/CC包、读取请求和带返回数据的ACK_Data包、写入请求和纯确认的ACK包。确认这些包都在再核对一下读到的数据里的字节序。西门子的S7通讯在PLC侧是按大端序来组织多字节数据的而上位机常见的是小端你在代码里如果直接按小端解析读到的双字十有八九是高低字节反的。抓包还有一个好处是能快速判断PLC返回的错误码。Wireshark对S7协议支持得比较完善解析时会直接标出返回码含义比自己在代码里打印一个整数再查手册快捷多了。就算没有WiresharkPLC侧诊断缓冲区里也会记录通讯相关的异常事件两边对着看基本能锁定问题方向。4. 现场故障排查与避坑实录4.1 常见故障现象与定位路线现场S7通讯问题翻来覆去就那么几种表现我把高频故障整理成了一张速查表排查时按表格从底层往上层定位效率高很多。故障现象常见原因优先排查方向TCP端口连不上IP配置错误、防火墙拦截、物理链路先ping再telnet 102确认网口状态TCP能连但S7握手失败连接资源耗尽、访问开关未开看PLC诊断缓冲区检查连接数连接成功但读不到DB数据DB优化访问模式、权限等级限制检查通讯DB是否非优化访问访问授权等级读到的数值总是错字节序不对、地址偏移算错抓包看返回字节核对地址映射表固定周期通讯中断交换机VLAN隔离、带宽冲突、网线质量抓包看是否重传检查网线两端协商速率偶发性通讯超时PLC扫描周期长、通讯缓冲区溢出检查通讯镜像区刷新逻辑降低通讯频率有一类问题很容易被忽略PLC连接资源被占满。工业现场经常是HMI、上位机、远程运维工具同时连到同一台PLC而像S7-1200这种级别的CPU可用的连接资源可能只有十几个甚至更少。连接资源耗尽时新连接不一定马上失败而是表现为“时通时断”特别迷惑人。遇到这种问题先在组态软件里看在线连接的资源占用把闲置的HMI连接和调试连接断开往往问题立刻解决。4.2 时间同步、DB优化访问与数据一致性单独把DB优化访问拿出来再强调一遍因为它真的坑人。S7-1200从固件4.0开始新建DB块默认就是优化访问模式这种模式下对外部访问并不友好。如果上位机用按地址偏移的方式去读经常读到的完全是无效数据。解决办法不是在通讯程序里打补丁而是在建块时就把“优化的块访问”选项取消。如果块已经建好可以在块属性里切换访问模式但一定要确认PLC停机切换时数据和引用有没有发生变化。数据一致性是另一个现场高频话题。当外部上位机一次性读取十个连续变量时PLC发送这些数据的过程不是原子性的可能先读了一半变量主程序又把另一半更新了这样上位机拿到的十变量快照在时间轴上不是同一时刻的。对大多数监控场景影响不大但涉及到联锁控制、配方下发时就可能引发误动作。稳妥做法前面提到过就是在PLC内做数据镜像区由PLC主动把一组相互关联的变量一次性复制到镜像区再让通讯功能只从镜像区读取。这样外部任何时候看到的镜像区都是逻辑上一致的快照。时间同步这个点看似无关实际上对S7通讯项目有潜在影响。PLC时钟不准最直接影响是日志分析对不上时间和顺序比如上位机记录一个报警发生时间PLC记录另一个时间排查时会对不上。设备数量多的时候最好在组态里配置NTP时间同步让PLC从统一时钟源对时。通讯本身不要求时间同步但一个现场系统里时间不一致早晚会误导你排查问题。4.3 调试工具与经验心得试过各种现场调试方法之后我现在固定下来的一套组合是Wireshark抓包做协议层定位PLC在线诊断缓冲区看系统事件再加上一个可配置的通讯测试工具做快速读写验证。这三样配合起来绝大多数S7通讯问题可以在半小时内定位到具体层。抓包时还有一些细节习惯。过滤条件不要只写tcp.port 102最好再加上跟踪流功能把同一连接的完整交互按顺序回放。因为S7通讯是典型的请求应答模式看单个包看不出时序问题但把整条流按时间轴看到能直观发现“请求发出去了但是没有应答”、“应答回来重复重传了”这类隐患。另一个习惯是保存现场抓包文件文件名建议按“日期_PLC型号_现象简述”命名不要用默认文件名不然几天之后自己都分不清哪个包对应哪个故障。最后想吐槽一个常见误区很多人在PLC程序里把通讯指令放在定时中断里觉得这样刷新足够快。但通讯指令执行本身的耗时和外部网络质量会互相影响如果定时中断周期设得比通讯处理时间长反而会造成通讯堵塞甚至溢出。我的习惯是让通讯指令在OB1里循环调用并把状态标志和错误代码完整记录到专门的诊断DB里外部可以随时查询最近一次通讯的错误码。这样做哪怕现场没有Wireshark也能从PLC侧看到明确的错误线索而不是对着一个“失败”干瞪眼。踩过几次坑之后我现在做S7通讯相关项目都会先做一件事用抓包工具把从建连到读写完成的全过程存一份基线包命名成“成功基线_某项目”等现场出问题时拿新包做对比差异一目了然。这套做法帮我在好几个项目里省下了一半的排查时间。S7通讯本身不算复杂但牵扯到协议层、组态层、程序层和物理网络层每一层都可能出问题。把分层的思维建立起来每一类问题你知道该去看哪一层这比记住多少具体报错代码都重要。
阅读完成 · 觉得有帮助?
咨询建站