用snap7搭建S7协议模拟器:server/partner/client调试实战 做工控上位机开发最头疼的事是手里没有真实PLC的时候程序根本没法调。你写了一个S7通讯模块总不能每次都跑去现场连设备开发期还好一旦到了联调阶段硬件没到位、程序出问题整个项目卡在那里干着急。所以我一直建议手边最好随时有一套能模拟S7协议的服务端再配一个趁手的调试客户端这样的组合能解决绝大多数协议联调问题。这里要聊的这套东西就是基于snap7做的S7协议调试工具/模拟器核心玩法就是标题里的三个角色server、partner、client。简单说就是在一台PC上同时跑起一个虚拟PLCserver用client去模拟上位机读写数据再用partner处理点对点双向通信。整套环境搭建起来后你可以随时启动一个“假PLC”开发调试时直接连它不用等硬件、不用占现场设备逻辑验证、压力测试、新员工培训都能在这个虚拟环境里完成。这篇文章我会从项目定位讲起把S7协议和snap7的基础理顺然后给出从零搭建模拟环境的完整步骤重点拆解client读写DB块/M区/I/Q区的调试套路以及partner模式如何做双向通信和主动推送还会把我在实际使用中踩过的坑和排查思路整理成速查表最后聊点把这套demo工程化、接入实际项目的扩展方案。1. 项目整体定位与设计思路1.1 这个工具解决的核心问题是什么S7协议调试这件事本质上是一个“没有PLC也要能干活”的需求。很多人以为写S7通信代码不复杂真正动手才发现光是TCP连接建立、TSAP协商、PDU读写这几个环节每一步都有不少细节。如果每次调试都要借助现场PLC效率极低而且有时候现场设备还在运行你连上去读写数据还可能干扰生产。snap7这套demo的价值就是把你平时要连的“真实PLC”抽象成一个可以跑在任意PC上的软件服务端让整个协议栈在本地完整跑起来。你写的客户端代码不用改任何逻辑只需要把IP从现场PLC改成127.0.0.1就能完成开发和测试。模块设计也很有代表性client负责主动建链和读写server负责监听和响应partner负责对等连接和双向交互三个角色刚好覆盖了S7通信中几乎所有的应用形态。有了这套环境你可以在没有硬件的情况下做几件实事验证上位机程序的逻辑、测试不同读写频率下的稳定性、模拟异常断线场景、评估PLC响应时间和数据延迟甚至让实习生快速理解S7协议的基本交互。对我个人来说最大的收益其实是节省现场调试时间很多低级错误在虚拟环境里就已经暴露出来了。1.2 S7协议与snap7基础概念梳理S7协议是西门子S7系列PLC的通信协议它并不是单纯的TCP应用而是基于ISO-on-TCP封装默认走TCP 102端口。通信过程大体分为三步先建立TCP连接再做COTP连接协商最后是S7通信服务协商之后才开始真正的读写请求。这个机制决定了连接建立不是“一次TCP握手就完事”而是有额外的协议协商开销这也是很多人在测“创建连接PLC需要多长时间”时觉得比预想慢的原因。snap7是一个开源跨平台的S7通信库采用C编写提供C/C、Python、Node.js、C#、Pascal等语言的接口。它在协议层面实现了客户端、服务端和伙伴三种角色这正是它适合当调试工具的原因。你不需要自己从头解析S7协议的PDU格式直接用它的API就能完成大部分操作。我个人的理解可以把snap7想象成“工控界的HTTP库”类似物。它把复杂的S7协议封装成了类似connect、read、write这样的简单接口而demo server又相当于一个本地HTTP服务器你拿客户端请求它就能看到完整的请求响应链路。这样无论是学习协议还是调试业务代码都有了一个非常干净的实验场。1.3 server、partner、client三种角色的分工这三个角色不是互相独立的炫技设计而是对应着实际工程中最常见的几种通信方式。client是主动发起方绝大多数上位机都属于这一类。它负责连接PLC、读取DB块数据、写入控制字典型的场景是WinCC、组态软件或自研的上位机界面。server是被动响应方snap7的server模式可以模拟一个简化版的S7 PLC它监听指定端口等客户端来连接。在这个角色里你可以注册多个数据区域比如DB1、DB2、M区、I区、Q区相当于模拟了PLC内部的存储区。它非常适合做模拟器让你的客户端代码在开发阶段就能完整跑起来。partner是对等连接模式它和client/server最大的不同在于它没有严格的“主从”关系两个partner之间可以互相发起连接、互相发送数据。这种模式非常适合模拟两个PLC之间点对点通信或者做上位机之间的双向数据交互。实际项目中我需要做设备的主动上报功能时就喜欢用partner来模拟这种“被动方主动发数据”的场景。在调试时最常用的组合是server client一个虚拟PLC配一个上位机客户端而遇到事件推送、双向心跳这类需求partner就派上用场了。把这三种模式都吃透可以说S7通信的大部分场景你都有了应对方案。2. 从零搭建一套可用的模拟调试环境2.1 snap7获取方式与环境准备snap7的获取有三种途径我按方便程度排个序。第一种直接用官方编译好的库文件。snap7官网和GitHub的release里提供了Windows、Linux、macOS的预编译库不想折腾的人下载对应平台的库配置到你的开发环境里就行在Windows上做C#开发或LabVIEW开发时尤其方便。第二种代码量不大、想自己控制编译选项的从GitHub拉源码来编译。snap7的源码在GitHub上可以找到Linux下执行make就能生成静态库和动态库同时还会生成几个可执行的demo程序。我自己在Ubuntu上编译过基本不会报错依赖很少。第三种做Python脚本类工具时直接用pip安装包名叫python-snap7。这个包已经把底层库一起打包了装好就能用我最常推荐给做测试脚本的同学。如果你在Windows下用Python直接pip install python-snap7就完事简单省心。如果你要编译demo那就需要准备C构建环境。整体来说这套环境对机器要求很低Win7往上、Ubuntu 18.04往上都能跑真·老设备也能胜任。2.2 编译并启动snap7 demo server我这里以Linux下编译源码为例演示一下。拉下代码后进入源码目录直接cd snap7 make -f build/debian/makefile编译结束在build/bin目录下会生成几个可执行文件其中snap7_server就是我们要的demo server。运行它sudo ./snap7_server默认监听0.0.0.0:102。这里有个细节102端口在Linux下属于特权端口普通用户没有权限绑定如果不加sudo启动大概率会报错所以我习惯上用sudo或者改成其他端口比如1102来规避权限问题。不过直接用官方编译出来的server有一个让人犯迷糊的地方你不知道它内部到底开了哪些数据区、DB块有多大。所以我实际调试时更推荐用Python自己写一个server脚本这样所有数据区域的大小和初始内容都能自己控制。import time import snap7 import snap7.server as srv from snap7.util import * server srv.Server() # 注册DB1大小1024字节初始全0 server.register_area(srv.areas.DB, 1, b\x00 * 1024) # 注册M区大小1024字节 server.register_area(srv.areas.MK, 0, b\x00 * 1024) server.start() print(模拟PLC已启动监听 0.0.0.0:102) while True: server.pick() time.sleep(0.01)这段脚本的关键点在于两点一是必须调用pick()让服务端持续处理入站请求否则客户端连上来也没人响应二是注册区域时data参数必须是一个bytes对象长度决定了这个区域的大小。如果你希望DB1里面预先放一些测试数据直接把bytes内容填成对应值就行。2.3 验证模拟器正常启动的三个手段服务端启动后不要急着写自己的代码先确认环境真的通了我用三个手段依次验证。第一个查端口。在Linux下执行netstat -tlnp | grep 102确认端口处于LISTEN状态。如果端口没起来第一步就得排查启动日志里的报错最常见的就是权限、防火墙或者端口被占。第二个用snap7自带的client demo连一下。在build/bin下通常也有snap7_client或者自己写一个最简单的Python客户端只要能成功连接就算通了。import snap7 client snap7.client.Client() client.connect(127.0.0.1, 0, 1) print(连接成功) client.disconnect()第三个用Wireshark抓包分析握手过程。Wireshark支持S7协议解码抓完包以后能看到COTP连接请求、S7 communication setup这些帧。我第一次看到完整的S7握手流程时瞬间就理解了为什么连接时间不只是TCP握手那几毫秒。这个过程对协议理解非常有帮助强烈建议做一次。2.4 与PLCSIM、真实PLC的适用场景对比有人可能会问为什么不用西门子官方的PLCSIM这里要分清两个工具的定位。PLCSIM是集成在博途TIA里的仿真器它模拟的是PLC内部程序的运行逻辑适合调试PLC程序本身而snap7的server模拟的是S7协议通信层面的行为适合调试上位机通信代码。打个比方PLCSIM是“模拟大脑”它能跑梯形图、跑逻辑但外部想要通过S7协议读写它的数据配置比较麻烦snap7 server是“模拟嘴巴”它有完整的通信接口无论你模拟的逻辑多简单它都能让外部设备按S7协议和它对话。我通常的做法是上位机通信模块用snap7环境调试真正PLC逻辑再用PLCSIM或现场设备验证两者互补而不是替代。3. client读写调试要点与常见操作套路3.1 建立连接的关键参数与连接耗时分析使用client模式时建立连接需要关注几个参数IP地址、rack号、slot号、TCP端口以及连接超时时间。client snap7.client.Client() client.set_connection_params(127.0.0.1, 0, 1) client.set_connection_type(snap7.types.ConnectionType.CONNTYPE_PG) client.connect(127.0.0.1, 0, 1)Python-snap7的connect(ip, rack, slot)其实等价于先设置参数再建立连接。rack和slot这是S7协议中用于定位PLC机架和插槽的对于真实PLCS7-300常见的配置是rack0、slot2而对snap7的demo server来说这两个值没有严格校验只要和server端一致就行demo server基本是任意值都能连。关于“创建连接PLC需要多长时间”这个问题我实测下来分两种情况。连本地snap7 server整个握手过程通常只有几十毫秒延迟几乎可以忽略。但连真实PLC或者通过工业交换机走远距离网络时连接时间会明显增加因为需要完成TCP三次握手、COTP连接协商、S7 communication setup三个阶段每个阶段都有一个RTT再加上PLC本身的响应时间所以你会看到连接耗时在几十到几百毫秒之间波动。如果现场网络丢包或负载高连接时间可能超过一秒这时候就需要在客户端设置合理的连接超时和重试机制比如把连接超时设为3到5秒失败后等待500毫秒再重试。3.2 读写DB块数据的常规流程读写DB块是S7通信里最常用的操作。一个完整流程是连接 - 按需读写 - 解析/拼装数据 - 断开连接或者保持连接进行周期通信。读取DB1的前16个字节只需要一行data client.db_read(1, 0, 16)这个请求的含义是读取DB块编号1从起始偏移0开始读16个字节。返回的data是一个bytes对象也就是说snap7给出的原始数据是“字节流”具体怎么解释成整数、浮点数、字符串是你自己要做的事。写入操作对称client.db_write(1, 0, b\x01\x02\x03\x04)这里要求写入的数据是bytes长度不能超过DB块剩余空间。如果写入的数据类型不是正好一整个字节比如要写一个bool或者一个8位整数就要先把它们拼成字节再写。这就是为什么我建议配合python-snap7提供的util模块来操作它能减少非常多的低级错误。读出来后要对数据进行解析最基础的工具是get_int、get_uint、get_real、get_bool这些函数from snap7.util import * value_int get_int(data, 0) # 从偏移0读取16位有符号整数 value_real get_real(data, 4) # 从偏移4读取32位浮点数 value_bool get_bool(data, 0, 7) # 取第0字节的第7位这几个函数的命名和用法非常直白但我还是想提醒一句西门子PLC的数据存储是大端字节序也就是高字节在前。如果你拿着小端的C/Java代码来直接强转读出来的数会完全不对。这个坑我见过太多次了包括我自己早期也被坑过写了个memcpy直接拷贝结构体结果浮点数怎么读都不对。3.3 M区、I/Q区以及定时器计数器的读写DB块之外S7协议还有其他几个存储区M区标志位、I区输入映像区、Q区输出映像区、C区计数器、T区定时器。snap7的client里读取这些区域一般用read_area和write_area。from snap7 import Types # 读取M区偏移0开始的10个字节 m_area_data client.read_area(Types.Areas.MK, 0, 0, 10) # 写入Q区偏移10开始的4个字节 client.write_area(Types.Areas.PA, 0, 10, b\x00\x00\x00\x01)这里要注意python-snap7里I区对应PEQ区对应PAM区对应MKDB块则使用db_read/db_write很多新手容易搞混。M区和DB区都是纯内存区域区别不大但I区和Q区在真实PLC里有硬件含义I区是输入信号一般只能读不能写Q区是输出信号写它会直接影响物理输出。不过在snap7 server模拟环境里I区和Q区没有硬件限制你怎么写都能成功这个特性在模拟测试时是好事但也要提醒自己真实PLC上千万别这么随便写否则可能触发设备动作。定时器和计数器的读写稍微特殊一些。S7协议里T区、C区的数据结构不是普通字节而是包含状态和计数值的复合结构。说实话直接用snap7读写这两个区域远不如把定时器、计数器状态映射到DB块或M区再由上位机读DB/M区来得方便。这也是很多真实项目的做法PLC程序主动把T/C值同步到DB区避免上位机处理复杂的区域格式。3.4 用辅助工具函数减少解析错误我写S7通信代码的过程中逐渐养成了一个习惯所有的字节序换算、类型转换绝不手写尽量用库或工具函数。手动处理很容易出现溢出、符号错误、字节序颠倒尤其在浮点数场景下错了之后还特别难排查。python-snap7的util模块里除了刚才提到的get/set系列函数还有set_int(data, offset, value)、set_real(data, offset, value)等写入辅助函数它们会自动处理字节序。如果你在用C#snap7也提供了对应的封装类比如S7Client下的DBRead/DBWrite以及S7.SetRealAt这类静态方法。此外我还会配合一个简单的“数据字典”来管理偏移量。比如把DB1设计成偏移地址数据类型含义0Real温度4Int压力6Bool运行状态8String[20]设备名称这样每个变量对应的地址和类型都清清楚楚写代码时直接查表比每次看plc程序找地址靠谱得多。实际上调试协议时最花时间的往往不是协议本身而是数据格式的对齐。4. partner模式调试细节与双向通信4.1 partner模式的定位与典型场景partner模式在很多人看来比较陌生我最初也习惯用client去轮询觉得没必要多学一个角色。后来做主动上报功能时才发现partner能省很多事。在client/server模式下数据流是单向的client主动请求server被动响应。如果我想实现“PLC主动把数据推给上位机”比如发生报警时PLC立刻通知上位机而不是等上位机来查询标准的client/server模型实现起来就很别扭要么上位机高频轮询要么把逻辑反过来——上位机作为serverPLC作为client主动连过来。而在真实项目中上位机的地址往往不是固定的让PLC主动连上位机不一定可行。partner模式解决的就是这个痛点。两个partner建立连接后通信双方是对等的任何一方都可以主动发送数据也可以随时接收数据。这样无论数据方向是哪边发起都能自然建模。典型的场景包括两台PLC之间的数据交换、设备端与边缘网关之间的双向通信、上位机模块之间的心跳和事件订阅。4.2 用python-snap7写一个最小partner例子partner模式的最小代码结构如下import time from snap7.partner import Partner # 节点A等待对方连接然后发一条消息 def nodes_a(): partner Partner() partner.start() partner.wait_as_server(5000) # 等待对方连接超时5秒 partner.send(bhello from A, 0) msg partner.receive() print(A received:, msg) partner.stop() # 节点B主动连接节点A接收消息后回复 def nodes_b(): partner Partner() partner.start() partner.request_connection(127.0.0.1, 0, 1, 5000) msg partner.receive() print(B received:, msg) partner.send(bhello from B, 0) partner.stop()这段代码里A先wait_as_server等待建链B通过request_connection主动去连A连接建立后双方就可以互相send和receive了。注意send的时候第二个参数是发送数据的长度在Python版本里传0表示由函数内部自动计算长度这个细节很多教程没讲清楚容易踩坑。partner模式与client/server相比麻烦的地方在于连接建立顺序。如果 B 连的时候 A 还没进入wait状态连接就会失败。所以实际项目里我会先把A端启动确认它在等待再启动B端顺序稳定了成功率就很高。4.3 回调机制与事件驱动的调试思路python-snap7的partner还支持回调函数发送和接收都可以挂回调这样不用阻塞式地等待消息对事件驱动型程序特别友好。def on_receive(partner, data): print(收到数据:, data) partner.send(back, 0) partner.set_recv_callback(on_receive)有了回调主场逻辑可以继续干別的事消息到达时库内部线程自动触发回调。这个机制有点类似socket编程里的异步接收但snap7封装得更隐晦很多文档没细说所以我建议调试partner时先写一个简单的“A发B收”的最小demo把回调打印加上确认消息能到达再往业务逻辑上叠加不要一上来就堆复杂状态机。4.4 partner与client/server同时使用时的注意点在同一个进程里同时跑server和partner或者同时跑多个partner实例需要注意线程模型和端口冲突问题。server默认监听102端口每个partner也会占一个本地端口如果都设为0系统会分配随机端口但你得确保对方知道往哪个端口连。我踩过一个典型问题在同一个Linux进程里先启动server再启动partner结果partner怎么都连不上server。排查了半天发现是本机的路由和端口绑定策略问题。后来我把server和partner放在不同进程里各自绑定固定端口问题就解决了。所以调试阶段能用多进程分离就尽量分离减少环境变量的干扰这也是我为什么强调“先搭最小demo再逐步复合”的原因。5. 常见问题与实战排查5.1 连接失败类问题速查我把调试S7连接时最常遇到的问题整理成了一张表基本覆盖了90%的情况现象可能原因处理建议连接超时IP地址不对、网络不通、服务端没启动先ping目标IP再确认服务端端口处于监听状态连接被拒绝防火墙拦截、端口占用、服务端未调用pick临时关闭防火墙测试用lsof -i:102查看端口占用连接成功但读写报错rack/slot参数不对、TSAP不匹配确认PLC型号对应的rack/slot尝试使用默认0/2或0/1客户端程序崩溃驱动库版本不匹配、多线程并发访问同一client实例升级/降级snap7库版本每个线程独立创建client实例5.2 数据读写错误类问题速查数据读错、写错、解析错这类问题占了我调试时间的70%。查得多了我发现本质原因往往只有几个第一个是区域编号与DB编号搞混。用db_read的时候第一参数是数据块编号而不是区域编号比如client.db_read(1, 0, 10)是读取DB1如果你把DB编号写成了0server就直接返回错误。第二个是偏移量算错。S7协议是字节寻址真实项目里常见的是DBD100表示偏移100字节处的Double WordDIX10.5表示第10字节第5位。换算成snap7的偏移参数时必须牢记“偏移量是字节数”。我见过同事把地址“1.5”直接当成偏移5来用结果数据永远对不上。第三个是数据长度不匹配。读取一个Real类型如果只读了2个字节解析出来的浮点数必然不对。所以我会规定自己的代码里每个变量的读写都配上一个明确的“长度常量”如REAL_LEN 4、INT_LEN 2用常量拼接字节流避免魔法数字满天飞。5.3 性能与多客户端并发问题关于性能我实测snap7 server在单客户端场景下顺序读写DB块的单次耗时基本在1毫秒以内。但如果有多个客户端同时连上来或者单个客户端开多个线程并发读写问题就会暴露要么server的pick()处理不过来要么client端因为网络延迟出现读写超时。如果你需要模拟多个上位机并发连接现场我建议不要用一个进程开一堆线程而是用多个独立进程每个进程里跑一个client实例。这样可以更贴近真实部署场景也避免Python GIL对并发性能的影响。server端如果性能吃紧可以通过调大server.set_param相关参数或者干脆把pick()的循环周期调短。5.4 环境差异与跨平台问题Linux下最突出的就是102端口权限问题普通用户无法绑定102以下的端口。解决办法有两种一是用sudo运行二是改监听端口比如1102同时客户端连接时指定client.set_param(Types.LocalPort, 1102)不总是有效最稳妥的办法是在connect时使用额外的端口参数具体看使用语言绑定支持程度。其实snap7的server在start之前有个set_param可以设置端口。Windows下则要留意防火墙弹窗第一次启动server时一定要允许通过专用网络否则客户端进程连不上。如果你需要用Docker跑snap7 server注意映射的端口也要同步而且容器内102端口同样有权限问题需要以root用户运行容器或者把端口映射到宿主机的非特权端口上。5.5 除了snap7还有哪些替代工具做S7协议调试并不只有snap7一条路。比如西门子官方的PLCSIM适合和博途配合做PLC程序仿真还有像HslCommunication这样的 .NET 通信库也内置了虚拟S7服务的功能适合集成到C#项目中。我自己的选择标准是纯自研、要灵活、要跨平台选snap7如果项目本身是C#且已经有HslCommunication依赖那就直接用它的虚拟服务可以减少引入新库的成本。再比如有人喜欢用昆仑通态、组态王这类组态软件自带的驱动配合一个虚拟PLC来测试原理和我这里讲的类似只是工具链更重。如果你是协议新手我更推荐先从snap7的demo入手因为它结构简单、可控性强你能看到每一步通信背后发生了什么。6. 从demo走向工程化的扩展思路6.1 把模拟器接入MES/SCADA/组态软件调试环境搭好以后别只用来跑跑自研脚本。snap7 server完全可以作为MES、SCADA、组态软件的数据源。比如你手上的上位机系统要对接一台S7-300 PLC但在现场设备到位前软件工程师可以先把自己的系统连到虚拟server上按真实PLC的DB块规划好数据区然后再开发业务功能。我以前接过一个项目MES系统需要从PLC采集几十个温度、压力数据当时现场PLC还在调试我就用snap7 server把DB规划好、初始数据填进去让MES开发同事直接对着虚拟环境联调。等现场PLC程序一稳定我们只需要把MES里的连接地址从127.0.0.1改成现场PLC的IP整个系统就切过去了几乎零差错。这一套流程走下来项目周期至少缩短了一半。如果你要连的组态软件走的是OPC那你再装一个OPC服务器把snap7读到的数据桥接到OPC里就行。本质上snap7 server提供了一个标准S7入口无论上层用什么协议都可以在中间层做转换。6.2 做一个HTTP/WebSocket转发网关我还在这个模拟环境基础上做过一个“协议网关”的扩展用snap7的client从server里读数据然后通过HTTP/WebSocket接口暴露给前端网页。这样做的好处是前端开发人员完全不需要懂S7协议只要通过接口请求JSON数据就行。核心思路其实很简单写一个后台服务循环读取DB块维护一个缓存字典然后前端请求时返回缓存里的值。当需要下发控制指令时前端POST一个JSON后台解析后调用db_write写入PLC。如果是要做报警推送再配合partner模式把报警数据主动推给WebSocket订阅者。这样一个架构有点像是给工业数据装了一个“翻译官”上层应用关注业务逻辑底层通信交给snap7去处理。我也建议有条件的团队把这类网关做成一键启动的开发工具放到公司内部工具库让所有做上位机开发的同事都能直接使用。6.3 用模拟器做自动化测试和协议压测联调之外的另一个大用途是自动化测试。你可以把模拟PLC的数据变化当成测试触发器比如修改DB1的温度值验证上位机告警逻辑是否正常再把M区某个位置置1验证复位流程。这些都能写成自动化脚本在CI里跑。我试过用pytest写一套针对S7通信模块的回归测试每次提交代码后自动启动snap7 server然后运行db_read、db_write、断线重连等用例。效果非常好相当于给通信代码加了一个线上监护。压测方面我写过并发脚本模拟20个客户端同时读同一个DB区域观察server的响应时间是否稳定这能提前发现并发瓶颈而不是等到了现场才被客户骂。6.4 从demo到可维护工具的几点建议如果要把这套东西做成团队公共工具我有几个具体建议。一是配置要外部化。DB块编号、数据区大小、IP、端口、读写频率别写死在代码里用配置文件或环境变量管理否则每个人拉下来都要改代码。二是日志要规范。S7通信涉及网络与数据排查问题时没有日志寸步难行。我通常在每个关键节点打印时间、操作类型、目标地址、字节数和执行结果格式保持统一方便grep。三是加一个可视化界面。哪怕只是一个简单的Tkinter界面把DB块数据用表格展示出来也能极大降低上手门槛。我见过有人把模拟器页面做成了类似HMI的界面可以直接在界面上修改温度、压力等模拟值业务人员看到直观又友好。四是版本管理。snap7的库文件在不同平台、不同语言下的行为有一定差异建议在项目里固定一个已知正常的版本不要随意升级。我维护的脚本里会在README里写明“当前验证过的snap7版本是X.X.X不要升级”。个人在实际使用中还有一个体会这套调试工具最大的意义不是省下了那几天的联调时间而是让我在写代码时心态更从容。知道手边随时有一个可控的PLC环境任何协议改动都可以立刻验证就不会因为调动不了硬件而拖延开发节奏。如果你也经常和S7协议打交道我强烈建议你把snap7 server client这套组合装到自己的开发环境里先跑通最小demo再逐步叠加业务场景你会上瘾的。