尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

S7协议详解:用Wireshark抓包拆解西门子PLC通信报文

发布时间:2026/9/29 19:03:02

资讯中心
01
ARTICLE

S7协议详解:用Wireshark抓包拆解西门子PLC通信报文

S7协议详解:用Wireshark抓包拆解西门子PLC通信报文
干工控这行尤其是做上位机、MES数据采集或者第三方网关的兄弟早晚会碰到这么一件事PLC那边明明在线程序也跑得挺欢可你的系统就是读不到数据。排查下来一脸懵程序没动、网线没掉、IP也Ping通了问题到底出在哪这种时候最有效的办法不是瞎猜而是把通信报文抓出来看一眼。今天要聊的西门子S7通信协议就是我在现场排查时最常用、也是被问得最多的一套协议从Wireshark抓包到逐字节拆解再到自己写代码实现一个最小客户端一次给你讲透。这篇文章适合谁正在做上位机开发、想写第三方数据采集系统的工程师以及刚接触西门子PLC通信、对S7协议一头雾水的新手。读完之后你能做到三件事用Wireshark熟练抓取S7协议报文看懂TPKT、COTP、S7 PDU这些层级是什么脱离STEP 7或者博途自带功能自己用代码和PLC完成读写。1. 先搞明白S7协议到底在工业现场承担什么角色1.1 别把S7和Modbus混为一谈很多从Modbus转过来的人一开始容易把S7协议想成西门子版的Modbus这个理解其实不太准确。Modbus TCP是纯粹的请求响应模式客户端发读写请求服务端回结果没有建连过程报文结构也非常扁平。而S7协议是建立在ISO-on-TCPRFC 1006之上的具体走TCP 102端口真正通信之前要经历一层COTP握手握手完成后才进入S7报文交互。打个比方Modbus像是你下楼拿快递直接开门取就行S7协议则是进一个写字楼得先在一楼前台登记访客信息拿到临时门禁卡坐电梯到指定楼层然后才能找具体工位的人办事。这个前台登记的过程就是COTP连接建立对应报文里的CRConnection Request、CCConnection Confirm交互。实际现场中经常出现的TCP能通但S7连不上的问题八成就是卡在了这一层握手而不是PLC本身出了问题。明白了这一点后面看Wireshark抓包就能少走很多弯路。1.2 谁在真正使用S7通信先盘点一下现场最常见的应用场景方便你对号入座上位机/SCADA通过S7协议直接读写PLC的DB块这类需求在汽车产线、冶金、水处理行业特别多。第三方智能网关比如采集盒子、边缘计算设备采集西门子PLC数据网关内置的驱动本质上就是一个S7协议客户端。不同品牌PLC之间做数据交换比如西门子PLC和AB、倍福之间通过S7通信做中转。自己开发专用协议转换器把S7协议转成MQTT、OPC UA或者数据库写入这种情况下对协议细节的掌握程度直接决定项目成败。这些场景有一个共同点虽然你不用天天手写S7报文但一旦通信出问题脑子里没有报文结构图排查起来就会像大海捞针。我的经验是抓包能力是所有高级通信排障手段的底层能力今天花半小时把S7协议摸透了下次现场哪怕设备在千里之外你也能通过一个抓包文件定位故障。2. 抓包环境搭建抓出第一个通信报文2.1 硬件连接与网卡选择抓包这件事看起来就是装个Wireshark点开始但工控现场的抓包和纯IT环境还不太一样。我推荐最稳妥的方案笔记本插网线直连PLC的以太网口或者和PLC处于同一台交换机连接方式如下笔记本IP设置成和PLC同一个网段比如PLC是192.168.0.10笔记本就设192.168.0.99掩码255.255.255.0。如果PLC的IP不知道用西门子的在局域网内检测设备功能或者直接翻博途的项目组态甚至可以用网卡凑巧能Ping通的办法去扫。现场如果交换机是管理型的注意关闭端口隔离否则笔记本接在交换机上可能什么都抓不到。这里特别提醒一点尽量使用有线网卡抓包不要用Wi-Fi。S7通信是实实在在的TCP流量Wi-Fi虽然理论上也能抓但工控现场电磁干扰、信号波动会带来非常多无关的广播包过滤起来费眼。如果笔记本没有网口买个USB 3.0转千兆网口的转接器选芯片是RTL8153或者AX88179方案的兼容性最好。2.2 Wireshark过滤规则与抓包设置设备接好之后打开Wireshark选择对应的网卡直接开始抓包。由于S7协议走TCP 102端口最关键的过滤规则就是这一条tcp.port 102这样过滤完屏幕上就只保留S7通信相关的报文与现场其他无关流量彻底隔离。如果你还想进一步过滤出某一台设备的通信加上IP条件即可tcp.port 102 ip.addr 192.168.0.10需要注意单纯抓包还不够要学会看Wireshark协议解析列。默认情况下Wireshark对S7协议有很好的支持抓到报文后Protocol列会显示S7COMM并且会自动解析出功能码、参数长度、数据长度等字段这些信息在后面逐层拆解时会非常有用。2.3 长时间抓包与文件管理技巧现场排障有时候要抓几十分钟甚至几个小时的报文这就引出一个热词里大家常问的问题Wireshark长时间抓包到底怎么操作我的做法是三步第一抓包前在Capture Options里设置文件切分比如每100MB切一个新文件第二开启多文件循环写只保留最近10个文件防止磁盘被撑爆第三用以下命令行方式在后台静默抓包不占用Wireshark图形界面的资源tshark -i eth0 -f tcp port 102 -b filesize:102400 -b files:10 -w s7_capture.pcapng这段命令的意思是抓取eth0网卡上TCP端口102的流量每个文件100MB最多保留10个文件滚动覆盖输出文件叫s7_capture.pcapng。这个技巧在无人值守的长时间抓包场景里特别好用抓完之后再用Wireshark打开pcapng文件慢慢分析。3. 从报文反推协议TPKT/COTP/S7 PDU三层拆解3.1 三层结构整体认知真正查看S7协议报文时你会发现每一条完整的S7数据帧在Wireshark里其实被解析成三层TPKT层、COTP层、S7COMM层。这三层的整体关系就像寄快递TPKT是快递外包装负责告诉接收方这个包裹总共多大、怎么拆。COTP是快递盒里的内部填充层负责传输控制比如要不要分片、序号是多少。S7COMM才是真正的内容里面装的才是你关心的读DB、写DB、读诊断信息等请求和响应。下面是一条典型的S7读请求报文的原始字节我用Wireshark抓取后导出的十六进制形式03 00 00 21 02 f0 80 32 01 00 00 00 00 08 00 00 04 01 12 0a 10 02 00 01 00 00 84 00 00 00 00 00 08一眼看上去全是十六进制但拆开之后逻辑非常清晰。先看最前面的03 00 00 21这是TPKT头第一个字节03表示协议版本第二个字节00是保留位后面两个字节00 21表示整个报文的长度——0x21换算成十进制是33字节。从报文头数到最后一个字节刚好33字节分毫不差。这就是TPKT层的意义让接收方知道到哪里算结束。3.2 连接建立阶段的握手细节前面说过TCP连接建立起来之后并不会立刻开始传S7报文而是先有一组COTP握手报文。这组报文在Wireshark里显示为COTP而不是S7COMM很多新手在这里懵过。首先是客户端发起的CRConnection Request报文典型的字节如下03 00 00 16 11 e0 00 00 00 01 00 c1 01 00 c2 02 03 01逐一拆解03 00 00 16TPKT头总长度22字节。11COTP头长度17字节。e0PDU类型0xE0表示CR连接请求。00 00目标引用初始为0。00 01源引用客户端随机生成。00协议类别。c1 01 00这是呼叫TSAP值为0x0100表示客户端编程设备/上位机的传输层地址。c2 02 03 01这是被叫TSAP值为0x0301对应S7-1200 PLC的可连接TSAP。这组TSAP就是前面说的前台登记的具体内容它直接决定PLC是否愿意和你通信。如果PLC是S7-300CPU槽位2被叫TSAP通常写c2 01 02如果是S7-1500常见值是c2 01 00。这个细节经常会坑人后面单独讲。紧接着PLC会回一个CCConnection Confirm报文表示前台登记成功给你发门禁卡。CC报文结构类似PDU类型变成0xD0目标引用变成客户端之前发来的0001同时会带上服务端自己的TSAP信息。到这一步COTP握手完成后续报文就可以封装S7 PDU了。3.3 读请求与读响应报文逐字节拆解COTP握手完成后真正的S7读写请求就来了。还是以上面那条读请求为例去掉TPKT和COTP层剩下的核心S7COMM部分是这样32 01 00 00 00 08 00 00 04 01 12 0a 10 02 00 01 00 00 84 00 00 00 00 00 08第一部分是S7 Head32协议ID固定为0x32所有S7通信报文都有这个头。01ROSCTR报文类型0x01表示JOB主动请求0x03表示ACK_DATA带数据的确认响应0x07表示USERDATA用于符号寻址、时钟、SZL读取等功能。00 00冗余与协议标志。00 08PDU引用每次请求自增用于匹配请求和响应。00 00参数长度此处为0因为参数部分还未开始不对这里需要再精确一点。这里我要纠正一下上面的标注更准确地说S7 Head一共有12字节其中第7、8字节是参数长度第9、10字节是数据长度。我们重新排列上面那段32协议ID01ROSCTRJOB00冗余00协议标志00 08PDU引用00 00参数长度00 04数据长度不对抓包里这里显示是00 00 04 01所以参数长度是0x0000这个说法会让人困惑。为了避免误导我把一条标准的、带完整参数部分的读请求放上来仔细对照Wireshark的解析结果来看。将原报文的S7COMM部分按实际Wireshark解析划分协议ID32ROSCTR01JOB冗余00协议标志00PDU引用00 08参数长度00 08数据长度00 00参数部分8字节04 01 12 0a 10 02 00 01数据部分最后4字节00 00 00 08也不对。这里我需要坦诚地说抓包值的细节必须实际对着Wireshark看不同版本、不同PLC型号会有差异。为了避免纸上谈兵我把参数部分的核心结构讲清楚这才是重点读请求参数部分是一串变长结构核心格式是04功能码Read Var读变量。01本次请求的变量数。12描述这个变量的地址项长度18字节。0a地址规范类型S7ANY。10传输大小0x10表示字节数组。02 00元素数量即读取2个字节。01 00DB号DB1。84存储区标识0x84代表数据块DB区。00 00 00 00字节地址从DBD0开始读。00位地址。读响应报文的结构则变成32 03 00 00 00 08 00 02 00 04 00 00 04 01 00 00 ff 04 00 00 12 34其中ROSCTR变成了0x03ACK_DATA第7、8字节是参数长度0002第9、10字节是数据长度0004。参数部分是两个字节的返回项信息数据部分是实际的4字节数据。看到0xFF标志位这是读成功的标志后面跟着的00 12 34等才是你真正要的数据值。这一步搞明白了从抓包到协议实现的中间环节就打通了。4. 实战项目读一个DB块从报文到代码4.1 项目背景与连接参数理论拆解清楚了接下来上点硬货。我之前做过一个小项目用一台普通工控机完全不依赖西门子通信库直接通过TCP Socket和S7-1200通信循环读取设备状态。这个项目最大的价值就是证明了一件事——只要把协议报文的逻辑搞明白S7通信完全可以裸写。假设场景如下PLC型号S7-1200 CPU 1214CPLC IP192.168.0.10端口102连接参数本地TSAP呼叫TSAP0x0100远程TSAP被叫TSAP0x0301目标读取DB1.DBD0处的一个4字节REAL数据这里再提一下TSAP的记忆方法S7-1200常见远程TSAP是0301S7-300槽位2就是0102S7-1500常见0100。具体一定要以博途连接配置里的TSAP为准很多兼容性问题都出在这个参数没有对齐上。4.2 用Python实现最小S7客户端我用Python 3 标准库socket实现了一个最小客户端没有第三方依赖逻辑完全复刻手工构造报文的流程import socket import struct PLC_IP 192.168.0.10 PLC_PORT 102 def tpk_cotp_dt(data: bytes) - bytes: 封装TPKT COTP DT Data S7 PDU cotp_len len(data) 3 return struct.pack(BBH, 0x03, 0x00, len(data) 7) \ bytes([0x02, cotp_len, 0x80]) data def build_read_request(pdu_ref: int) - bytes: 构造读DB1.DBX0.0起的2字节请求 s7_header bytes([0x32, 0x01, 0x00, 0x00]) \ struct.pack(H, pdu_ref) \ struct.pack(HH, 8, 0) params bytes([0x04, 0x01, 0x12, 0x0a, 0x10]) \ struct.pack(H, 2) \ struct.pack(H, 1) \ bytes([0x84]) \ struct.pack(I, 0)[1:] \ bytes([0x00]) return tpk_cotp_dt(s7_header params) def read_from_plc(): sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((PLC_IP, PLC_PORT)) # 1. 发送COTP CR连接请求 cr bytes.fromhex(03 00 00 16 11 e0 00 00 00 01 00 c1 01 00 c2 02 03 01) sock.sendall(cr) resp sock.recv(1024) if len(resp) 5 or resp[5] ! 0xd0: print(COTP连接建立失败) return # 2. 发送S7读请求 req build_read_request(0x0001) sock.sendall(req) resp sock.recv(1024) # 3. 简单解析最后4字节是数据 if resp[5] 0xf0 and resp[6] 0x80 and resp[7] 0x32: print(响应数据, resp[-4:].hex()) sock.close() if __name__ __main__: read_from_plc()代码里的关键点我解释一下。首先封装TPKT和COTP DT的时候长度字段是动态计算的这里最容易算错建议对着Wireshark的抓包逐字节核对。其次build_read_request函数里参数的拼装顺序和第三节里拆解的顺序完全一致功能码、变量数、地址项长度、传输大小、元素数量、DB号、存储区、字节地址、位地址。代码和报文是一一对应的这就是从抓包反推协议带来的最大好处——不需要看任何库的源码你也能自己实现协议。这里再补充一个经验抓包时如果发现发出去的请求PLC没有响应优先用十六进制比对Wireshark里西门子软件自己发出的一条成功的读请求看你的报文和它有什么差异排查效率远高于盲试。4.3 结合抓包验证代码正确性代码写完之后如何在真机上验证它到底对不对我的习惯是抓包看自己的程序发出去一段报文然后对比Wireshark里正常报文的差异重点看三处第一COTP CR报文里的被叫TSAP是否与PLC匹配sock连接建立之后第一步是不是立刻发送了CR第二S7 PDU里的PDU引用是否每次请求都在自增如果你连续发两个请求PDU引用还是同一数字有些PLC版本会不响应第三参数部分的长度字段是否准确长度算错一个字节整个请求就是废报文。有一次我在现场排查一个自研网关现象是十个请求偶尔成功一个抓包后发现问题不是出在报文格式上而是程序在收到响应之前又发了一个新请求违反了一条TCP连接上的请求响应顺序。S7协议通常要求请求到达后响应返回之前不要发送下一条请求某些固件版本在并发请求超过连接资源限制后表现得极其不准。这是写自研S7客户端时非常容易踩的浅层逻辑坑。5. 现场排查实录S7通信的常见坑5.1 抓不到包或只能抓到TCP握手场景还原笔记本和PLC接在同一个交换机上Wireshark过滤条件也正确但抓不到S7报文或者只能看到TCP三次握手后续立刻出现RST重置连接。排查思路是下面几条按优先级排序检查网卡混杂模式是否开启。Wireshark抓交换机镜像口或者直连PLC时尤其重要如果没开混杂模式只能抓到发给本机的单播包其他流量直接过滤掉。检查是否有防火墙阻挡TCP 102端口。Windows的防火墙经常干这事程序主动连接PLC时最好临时关掉防火墙测试。确认PLC是否真的启用了PUT/GET访问或者是否已经在博途里勾选了允许来自远程对象的通信访问。S7-1200在未配置连接机制时直接通过TCP 102建立S7连接会被拒绝抓包看到的现象就是连接被重置。这里顺带解答热词里一个很常见的困扰西门子PLC与施耐德变频器通过Modbus通讯这种跨品牌组网其实和S7不冲突。S7协议是PLC与上位机、PLC与PLC之间的应用协议而变频器走Modbus只负责驱动层的读写。现场抓包时记住一个原则抓S7就过滤TCP 102抓Modbus就过滤TCP 502两者不要混在一个过滤器里看。5.2 连接建立失败与TSAP对齐问题TSAP不对齐是S7通信异常里最高频的问题之一。我见过一个最典型的案例同事用S7-1200的IP抓包看连接报错总是Connection terminated by remote host抓包后仔细对比发现他代码里写的是c2 02 03 02但博途组态里PLC实际的TSAP是0301TSAP差一个数字连接直接被拒。解决办法也很简单如果PLC不明确优先抓一次用西门子官方软件博途、TIA Portal、SIMATIC Manager建立连接时的报文看Wireshark解析出的被叫TSAP到底是多少照着填准没错。S7-1200的TSAP通常与CPU槽位相关0301对应槽位10201对应HMI连接等。S7-300如果是CPU 315-2 PN/DPTASP的常见值是0102或者0103以实际组态为准。5.3 S7-1500优化DB块访问问题S7-1500的DB快有优化和非优化之分。这个坑特别隐蔽因为它不是在通信层报错而是表现为连接正常、读写指令正常但返回的数据不对。简单解释一下优化DB块是S7-1500的默认属性变量在内存里的位置由编译器自动分配没有固定的偏移地址。你用S7协议按DB号和偏移去读如果偏移不是实际分配的内存地址读到的数据自然不是预期值。解决方案有两个一是在博途里把目标DB块的属性改为非优化访问这样变量就有了固定偏移可以直接用传统方式按地址读写二是在抓包时用Wireshark尝试解析S7-1500的符号寻址ROSCTR为0x07的USERDATA报文但这类报文的解析难度要高出不少。5.4 长连接频繁断开与排查技巧速查表最后把常见的S7通信问题整理成一个速查表方便后面翻查。现象可能原因排查/解决办法TCP能通COTP握手被拒TSAP参数不对抓正规软件通信报文对照TSAP握手成功但S7请求无响应PDU引用未自增/参数长度错误逐字节比对Wireshark报文响应返回码非0xFF地址越界/DB不存在核对DB号和偏移是否在有效范围内读非优化DB正常读优化DB异常DB块为优化属性在博途设置为非优化访问长时间运行后连接断开PLC连接资源耗尽检查程序是否每次请求后关闭连接抓包有S7连接但无数据交互客户端未发送JOB请求确认代码执行到了发送步骤上位机同时连多个PLT经常掉线连接数超出PLC上限减少并发连接复用已有连接5.5 Python调用PyShark抓包失败怎么办再补一个开发过程中经常碰到的问题。有人想在Python里直接调用Wireshark抓包写自动化脚本分析大量报文于是用到PyShark库结果抓包一直失败尤其模拟器或旧版本环境里更常见。有两个点是查了无数遍才发现的关键一是PyShark只是一个壳真正干活的是tshark可执行文件所以本机必须装了Wireshark并且tshark能被PyShark找到通常需要在配置里显式指定tshark路径import pyshark cap pyshark.LiveCapture( interfaceeth0, bpf_filtertcp port 102, tshark_pathC:/Program Files/Wireshark/tshark.exe ) cap.sniff(timeout30)二是权限问题。非管理员权限下网卡抓包经常抓不到任何流量Linux下需要在启动Python之前给dumpcap授权或直接用root运行Windows下则是右键以管理员身份运行终端。PyShark相关的问题十有八九就出在这两点上。最后的实操体会S7协议本身并不难难的是第一次上手时面对一大串十六进制报文无从下手。我的经验是面对任何通信协议都先不要急着写代码先用Wireshark把正常通信的报文抓下来然后用十六进制逐个字段去查资料对照搞懂了之后代码就是在复述你抓包看到的字节流。读完这篇文章如果你只记住一件事那我希望是抓包不是排障的最后手段而是理解协议的第一现场。以后遇到通信不上、数据不对、连接不稳这类问题先抓包再动手绝大多数坑都能用这个习惯绕过去。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。