STM32上实现EtherNet/IP从站:协议栈移植与实战踩坑指南
发布时间:2026/9/3 2:38:39 作者:尧图编辑部 阅读量:1,286

简介面向嵌入式开发者的STM32 EtherNet/IP协议实现项目基于lwIP协议栈与CIP核心服务并结合ChibiOS实时操作系统与HTTP服务目的是让STM32以标准工业以太网节点身份接入自动化网络为工业自动化和物联网场景提供一套完整的网络通信参考方案。资源包共467个文件以C/C源码和头文件为主211个h、177个c涵盖STM32F4底层驱动、lwIP网络接口、CIP对象与消息解析、套接字通讯及HTTP服务另有PDF文档、配置文件、构建脚本与README说明整体压缩包仅1.74MB便于快速下载与离线研读。目前已有6699人学习下载适合具有嵌入式基础、希望进阶工业以太网协议的开发者。通过示例工程可掌握在STM32F4平台上如何初始化lwIP、实现EtherNet/IP的CIP显式与隐式消息传输、理解CIP数据结构和套接字编程并参考RTOS下的任务划分与资源管理方式对工业网关、设备联网等实际项目落地具有很强的借鉴意义。1. 为什么要在STM32上跑EtherNet/IP从站设备厂商的“自研冲动”与真实成本先把话说在前面如果你只是想给自己的设备接上罗克韦尔PLC我大概率会建议你直接买一个Anybus通信模块别自己折腾。但在过去几年里我经手过好几个项目最后都走上了“在STM32上自研EtherNet/IP从站”这条路原因也很现实——模块单价高、交期不稳定、大批量出货成本压不下来而且模块作为中间层总会带来一些数据吞吐和诊断信息上的限制。EtherNet/IP在工业以太网里是绕不开的存在。北美市场尤其是汽车、包装、半导体设备行业罗克韦尔的ControlLogix和CompactLogix几乎就是标配。这些PLC原生支持EtherNet/IP协议而不是Modbus TCP。你只要接这种现场就必然要面对它。很多人听到“EtherNet/IP”第一反应是“这不就是以太网加IP协议吗跟Modbus TCP差不多”。我一开始也这么想实际上被按在地上摩擦了很久。EtherNet/IP真正复杂的地方不在TCP/IP那一层而在它承载的CIPCommon Industrial Protocol通用工业协议对象模型。CIP是一套完整的、面向对象的设备描述体系有对象、实例、属性、服务还有基于连接的通信调度机制。更直白地说Modbus TCP是在传送一堆寄存器地址和数值而EtherNet/IP是在跟一个“设备对象模型”对话。这个项目适合谁适合那些需要自研工业从站设备、批量出货、并且有一定的STM32和TCP/IP协议栈开发经验的工程师。硬件平台我建议直接上带以太网MAC的型号比如STM32F407、F429、H743配一颗PHY芯片LAN8720A最常用便宜稳定。如果你手里只有F103这种没有MAC的型号也不是不行外挂SPI接口的W5500是另一条路后面我会专门对比这两种方案的差异。在这篇文章里我会完整梳理EtherNet/IP从站的协议栈结构、方案选型、移植步骤、实测踩坑和稳定性优化最后给出我个人在几个项目里沉淀下来的排查经验和建议。整个实现过程跨硬件、协议、RTOS、PLC联调几个层面内容不少尽量讲得干一些。2. EtherNet/IP协议栈分层拆解TCP/IP之上CIP对象模型才是灵魂EtherNet/IP不是单一协议它是一整套协议栈的组合。从下往上大致是物理层以太网PHY、数据链路层MAC、网络层与传输层IP、TCP、UDP、EtherNet/IP封装协议层、CIP对象层、最后是上层应用对象。绝大多数STM32开发者熟悉的是前四层真正需要花时间啃的是最后两层。2.1 贯穿全场的CIP对象模型CIP对象模型是整个EtherNet/IP的基石。一个CIP设备在网络上是以“对象集合”的形式存在的每个对象有明确的Class ID类ID、Instance ID实例ID和Attribute ID属性ID。通讯时主站Scanner通过发CIP服务请求来读写这些对象的属性或者调用对象支持的服务。用大白话讲就是一个设备被拆成了一堆“抽屉”对象每个抽屉里有多个“文件”实例文件里有具体的“字段”属性。主站要什么你得知道去哪个抽屉、哪个文件、哪个字段取。对于EtherNet/IP从站ODVA规范要求必须实现以下核心对象Identity Object0x01设备身份信息包括厂商ID、设备类型、序列号等Message Router Object0x02消息路由负责分发显式消息Assembly Object0x04数据映射I/O连接的数据读写都走它Connection Manager Object0x06连接管理器处理Forward Open/Forward CloseTCP/IP Interface Object0xF5IP地址、网关、DNS等网络配置Ethernet Link Object0xF6物理链路状态、速率、MAC地址等其中Assembly Object和Connection Manager是I/O通信隐式消息的核心TCP/IP Interface和Ethernet Link则主要被主站用来管理网络。如果这四个对象实现得不对设备能“ping通”但PLC一扫描就是找不到设备。2.2 显式消息与隐式消息两种完全不同的通信方式EtherNet/IP把通信分成两类理解清楚这个区分是整个项目思路的关键。显式消息Explicit Message走TCP 44818端口特点是“一问一答”适合读配置、改参数这类非实时操作。比如PLC读取设备序列号发送一条CIP Get Attribute请求设备收到后回一条响应。这类消息对实时性要求不高允许偶发延迟。隐式消息Implicit Message走UDP 2222端口特点是“周期性”适合实时I/O数据交换。建立连接时主站会发起Forward Open服务协商一个RPIRequested Packet Interval请求包间隔比如10ms或20ms。之后设备要按照RPI周期性发送I/O数据主站也要按这个节奏接收。这类消息的实时性要求很高一旦错过几个周期连接就会被判定超时断开。可以用一个职场场景来类比显式消息就像你给同事发邮件问“上个月报表数据是多少”对方回复一封邮件即可隐式消息就像两栋楼之间开了一条定时班车线每天8点、10点、14点准时发车错过了就得等下班车连着错过几班整条线路就停运了需要重新申请开通。2.3 数据在线上到底长什么样EtherNet/IP报文的核心结构是Ethernet帧头 → IP头 → TCP/UDP头 → EtherNet/IP封装头 → CIP报文。封装头里有协议版本号、命令码、会话句柄、状态等字段。这里要特别强调一个最容易踩的坑字节序。EtherNet/IP报文主体使用小端字节序Little-Endian而Modbus TCP使用的是大端字节序Big-Endian。如果你做惯了Modbus TCP到了EtherNet/IP这里还按大端处理数据错位几乎是必然的。STM32本身是小端处理器这一点相对友好但如果你用的是LWIP协议栈TCP/IP头里有些字段是大端的处理时要格外小心区分。很多“数据全是乱的”问题追根究底都是字节序搞反了。I/O数据本身是“无头数据”没有额外的长度、类型字段就是约定好的字节流。设备在Assembly对象里把输入数据和输出数据分别映射成特定的字节数组主站通过连接ID引用。3. 方案选型开源协议栈、商业SDK与“从零造轮子”的真实边界在动手写代码之前先花点时间把方案选型的账算清楚。这个决策直接影响后续几个月的开发节奏和产品最终形态。我见过不止一个团队一开始雄心勃勃要“全自研”写了三个月发现CIP对象模型细节太多最后又回头买商业协议栈时间白白浪费掉。3.1 开源方案OPC基金会NIO/KPA原OpENer目前最主流、也最靠谱的开源EtherNet/IP从站协议栈是OPC基金会维护的EtherNet/IP工具包早期叫OpENer2021年后拆成NIO核心协议库和KPA应用层框架两部分。这个库是很多商业协议栈的起点代码质量不错文档也相对完整。NIO库的架构比较清楚底层有平台抽象层Platform Abstraction包括socket收发、定时器、文件操作等接口需要你根据目标平台去实现上层是CIP对象管理器、连接管理器。我目前对接过的STM32项目里OpENer是最常被采用的起点。它的实现完整度足够过ODVA一致性测试对Assembly对象的支持也好。缺点是代码量偏大纯C实现的NIOKPA大概有几万行代码初次移植时要花不少时间来梳理。3.2 商业方案省事但费钱商业协议栈比如HMS的EtherNet/IP SDK、Pyramid Solutions的NetEPI、AC500系列等一个共同特点是授权费不低按产品型号收版权费出货量大了是一笔不小的开销。商业SDK的优势是经过了大量客户验证兼容性更稳定一般还附带完整的技术支持。如果产品定位是中高端工业设备量产数量不多单台利润能覆盖授权费买商业SDK其实很划算省下的开发时间可以投入到业务功能上。如果产品要走量比如一年出货几万台授权费就很可观了那时候自研或者用开源方案就更有吸引力。3.3 完全自己写到底行不行“从零造轮子”有没有可行性有前提是你有足够的时间和精力而且项目周期允许。EtherNet/IP从站协议栈的核心工作量不在于收发报文而在于CIP对象模型的完整性和健壮性。ODVA规范叠起来像一块砖头里面描述了大量细节连接状态机、超时处理、错误码、异常分支、消息路由规则、封装会话管理……每一个细节都可能在现场踩雷。从成本角度粗算一个人全职开发从零写一套能稳定过一致性测试的从站协议栈乐观估计6到9个月基于OpENer移植1到2个月能跑到能跟PLC通信买商业SDK集成工作大概2到4周。工程预算里人力成本和项目延期风险往往被低估。如果你不是特别硬核的协议研究者我更倾向建议基于开源NIO/KPA移植这几乎是自研和成本之间的最优解。3.4 表格三种方案对比方案授权费开发周期到PLC能通信代码可维护性过一致性测试难度适用场景开源NIO/KPA无4~8周中上需要二次封装中等大批量出货、有研发预算的自研产品商业SDK按套或按出货量2~4周好封装完整低中高端设备、量产数量不大、追求可靠完全自研无人力成本极高6~9个月初期差后期看个人高学习研究、协议深度定制4. 移植落地从STM32 LAN8720环境到第一个“能连接”的从站选定了方案之后接下来的事情就是把协议栈“搬”到STM32上跑起来。我以基于OpENer/NIO的移植路径为例完整梳理一遍从硬件搭建到代码集成的全过程每一步都给出我实测下来的经验和注意事项。4.1 硬件平台选择内置MAC与W5500的两难STM32系列里适合跑EtherNet/IP的型号基本集中在带以太网MAC的型号上比如F407、F429、H743等。这些型号通过RMII或MII接口连接外部PHY芯片我用得最顺的是LAN8720A价格便宜、资料多、发热小而且和STM32CubeMX的适配几乎是开箱即用。有人可能会想用W5500这种集成硬件TCP/IP协议的芯片来“省事”实际效果恰恰相反。W5500的硬件协议栈帮你处理了TCP/IP层但EtherNet/IP协议栈要用的是socket接口读写CIP报文W5500的硬件缓冲管理和socket数量限制反而是累赘。而且CIP连接是周期性的W5500在缓冲区控制上不如直接用STM32内置MAC LWIP来得灵活。我踩过一次坑后来老老实实换回了F407 LAN8720。STM32H743是我个人最推荐的型号主频高、RAM大跑TCP/IP协议栈加上CIP对象管理性能余量充足。F407也够用但处理大报文和频繁中断时CPU占用率会偏高。4.2 CubeMX工程配置ETH、LWIP、FreeRTOS一套配齐工程骨架用STM32CubeMX生成关键配置如下ETH外设选择RMII接口PHY地址根据LAN8720A的硬件配置设置通常是0x00LWIP开启作为TCP/IP协议栈基础FreeRTOS开启任务调度交给RTOS时钟ETH的RMII需要50MHz参考时钟一般从STM32的MCO引脚输出给PHYETH初始化的核心代码大致是// 在MX_ETH_Init()中PHY地址为0x00RMII模式 eth_handle.Instance ETH; eth_handle.Init.MACAddr[0] 0x02; eth_handle.Init.MACAddr[1] 0x00; eth_handle.Init.MACAddr[2] 0x11; eth_handle.Init.MACAddr[3] 0x22; eth_handle.Init.MACAddr[4] 0x33; eth_handle.Init.MACAddr[5] 0x44; eth_handle.Init.MediaInterface ETH_MEDIA_INTERFACE_RMII;LWIP配置里我个人建议打开DHCP作为调试期选项但正式产品最好固定IP。EtherNet/IP设备本身是支持DHCP的但工业现场大量使用固定IP规划现场改地址的场景非常少固定IP更可靠。4.3 移植OpENer/NIO的关键步骤OpENer移植的核心工作在平台适配层。需要实现的主要有socket收发把OpENer的socket接口对应到LWIP的netconn或socket API时间戳OpENer的CIP连接管理需要精确时钟来判断超时和RPI调度H743自带高精度定时器用定时器提供毫秒级或微秒级时间戳任务并发CIP连接管理线程和LWIP接收线程需要合理划分OpENer里和平台相关的内容主要集中在platform目录下主要接口大致是// 平台抽象层关键接口 int opener_socket_udp_create(...); int opener_socket_tcp_create(...); int opener_socket_sendto(...); int opener_socket_recvfrom(...); uint64_t opener_get_time_ms(void);把这几个接口用LWIP实现后OpENer就可以跑通了。我们还需要为RTOS创建几个核心任务// LWIP TCP/IP处理任务 void lwip_tcpip_thread(void *arg) { // 调用lwip_init()后进入tcpip_thread循环 } // EtherNet/IP数据处理任务 void enip_app_thread(void *arg) { // 创建TCP监听44818UDP绑定2222 // 循环调用opener_process_packets() }这里有一个我认为很重要的经验不要把EtherNet/IP的报文处理放在LWIP的tcpip_thread里。LWIP的回调线程应该只做协议栈的基础处理CIP对象解析和应用层逻辑放到独立的高优先级任务里避免互相阻塞。4.4 配置Assembly对象I/O数据映射的关键EtherNet/IP的数据交换最终都落在Assembly对象上。需要根据你的设备定义输入Assembly和输出Assembly并明确各个字节位置对应什么变量。比如一个简单的数字量IO从站// 输入Assembly从站发给主站8字节 uint8_t input_assembly[8]; // 输出Assembly主站发给从站8字节 uint8_t output_assembly[8]; // 初始化Assembly对象 void app_assembly_init(void) { // 注册Assembly实例ID比如输入Assembly实例100输出Assembly实例101 assembly_register_instance(100, input_assembly, sizeof(input_assembly), 8, 1); assembly_register_instance(101, output_assembly, sizeof(output_assembly), 8, 1); }Assembly实例的编号规则要和你后续在PLC组态里填的输入输出实例号保持一致这里是最容易出错的地方之一。我在测的时候因为实例编号填错PLC扫描不到设备一度以为是自己协议栈没移植好排查了整整大半天。4.5 用Python cpppo先跑通基础通信在接PLC之前强烈建议先用cpppo这个Python库模拟主站验证从站端的响应是否正确。cpppo可以实现一个简单的EtherNet/IP Scanner用代码把协议交互逻辑跑一遍。from cpppo.server.enip import client # 连接从站设备读取Identity对象 with client.conn(host192.168.1.10) as conn: # 读取Identity对象序列号 serial conn.read((0x01, 1, 7)) print(Device serial:, serial)这个步骤能帮你把协议栈本身的问题和PLC组态的问题剥离开。cpppo验证通过以后再上真实的PLC联调排查范围就小很多。5. 从“能ping通”到“PLC正常读写”实战踩坑记录与排查链路EtherNet/IP从站最折磨人的阶段就是“网络能通但PLC死活找不到设备”的那段时间。这里我把自己实际碰到的几个坑完整复盘出来每个都给出现象、排查过程、根因和解决办法希望能让你少走几个月的弯路。5.1 PLC扫描不到设备Ethernet Link对象Device Type没填对现象设备在网络上能ping通用第三方扫描工具也看不到设备或者能看到但识别不到设备类型。排查过程先用Wireshark抓包看设备上电后是否主动发送EtherNet/IP的发现报文List Identity然后再看PLC的扫描过程。抓包发现设备其实在响应但响应报文里的设备类型字段返回的是一个不合理的值。根因Ethernet Link对象里有一个Device Type字段用来标识设备类型。OpENer初始化时如果这个字段的默认值跟你实际产品不符PLC侧的扫描结果就会异常。有些PLC对设备类型识别很敏感会直接判定“设备不可识别”。解决办法按照设备实际类型填充Device Type字段。比如你的设备是数字量IO从站就要查ODVA规范里对应的设备类型编号填进去。这个字段也不是随便填建议参阅ODVA的公开规范里各产品类型编码。5.2 Forward Open返回错误Assembly实例号对不上现象从站能被扫描到但PLC组态里建立I/O连接Forward Open时一直报错连接建立失败。排查过程把PLC的故障代码记录下来回头查找CIP的Connection Manager对象返回的错误码。有一次我遇到的错误码直接指向“目标对象不可用”说明Forward Open里指定的目标路径路径里包含Assembly实例号没有对应到有效的Assembly对象。根因我定义的Assembly实例号和组态里配置的实例号不一致。PLC组态里输入Assy实例填的是100输出Assy填的是101但我代码里注册的是101和102差了一位导致Forward Open请求路径找不到对应的对象。解决办法把Assembly实例号统一。建议在PLC组态和代码里都用同一个约定好的编号规则比如输入100、输出101形成固定习惯。5.3 I/O连接周期断开RPI 10ms扛不住现象PLC组态里设置了10ms的RPI刚开始通信正常几分钟后连接频繁断开重连有时候干脆连不上。排查过程先用cpppo连接抓包看UDP 2222端口的数据收发节奏。发现从站在RPI周期内有时无法及时发送数据包超时时间一到主站判定连接超时自动断开。根因代码里CIP报文处理任务的优先级不够或者LWIP接收线程和CIP解析处理线程之间协商不顺畅。当时我把EtherNet/IP报文处理放在了一个较低优先级的任务里如果同时有大量显式消息比如PLC在轮询设备诊断信息I/O数据包就会被延后等到超时。解决办法调整RTOS任务优先级确保CIP数据包处理任务优先级高于普通的网络管理任务。同时要确保I/O处理路径上没有阻塞操作比如文件读写、打印调试等。5.4 数据错位与字节序混乱AB PLC里的INT16数组错乱现象连接正常数据也流动着但PLC和从站看到的数值对不上比如数组错位、整数高低字节反了、负数变成很大的正数。排查过程先在从站端把收到的原始字节打印出来和PLC实际发的值对比。发现两个问题叠加一是EtherNet/IP报文里CIP数据用的小端序我没有转换直接把MODBUS的思维带过来了二是Assembly里定义的数据长度和PLC组态里配置的不一致导致索引对不上。根因字节序处理不当以及Assembly数据布局和PLC组态不匹配。解决办法规范数据处理流程统一在从站的应用层做一次字节序转换把EtherNet/IP网络的报文数据转成STM32内部使用的数据格式再交给应用逻辑。同时在PLC组态里严格按照设备说明书来配置数据类型和长度两边保持一致。5.5 显式消息读写标签失败Message Router响应要小新现象PLC通过显式消息比如MSG指令读写从站设备参数有时成功有时报错且错误码没有规律。排查过程逐条抓包对比发现失败场景都集中在“多个显式消息同时到达”的情况下。OpENer的Message Router处理是单线程的多个请求并发时后面的请求被简单丢弃从而表现为偶发失败。根因Message Router缺少请求队列并发处理能力不足。虽然在正常产品使用中显式消息频率不高但如果PLC的例程里密集使用MSG指令并发概率就不低了。解决办法在OpENer的消息处理入口增加一个队列把收到的请求缓存起来依次处理。对响应超时不敏感的应用这是最简单有效的方案。以上这些坑几乎每一个都让我在调试阶段熬过一两个通宵。但好在每次踩坑对协议的理解就会深一层。尤其推荐养成“先抓包、再分析、后改代码”的排查习惯别凭感觉猜。Wireshark加上直观的报文解析窗口能让EtherNet/IP的调试过程直观很多。6. 实时性与可靠性设计当从站不是“能跑”而是要“稳”从开发板上跑通EtherNet/IP到产品在产线上稳定运行中间还有一道很重要的坎可靠性。工业现场对通信实时性和稳定性的要求很高10ms的RPI、持续7x24小时运行、断线重连、异常恢复这些场景都需要提前考虑。6.1 RTOS任务划分与中断优先级设计从实际项目经验看任务划分建议如下最高优先级ETH中断接收数据包触发次高优先级CIP数据包处理任务负责CIP报文解析和Assembly数据更新中间优先级TCP/IP管理任务LWIP tcpip_thread较低优先级应用逻辑任务、诊断任务ETH中断里只做数据包接收和信号量释放不要在中断上下文里做CIP解析。LWIP的tcpip_thread用来维护TCP连接状态处理显式消息的收发。CIP任务只处理跟实时性相关的I/O数据一旦任务被阻塞RPI就很难保证。一个关键点是FreeRTOS的定时器服务任务如果需要处理RPI相关的周期发送注意定时器任务的优先级。我在项目里用了专用的RTOS软件定时器优先级高于普通任务且定时器回调里只做标记和数据拷贝实际发送操作放在CIP任务里处理。6.2 超时处理与断线重连策略EtherNet/IP的连接管理机制里超时是常态而不是异常。从站要正确处理连接超时后的状态机切换确保旧的连接资源被释放新的Forward Open请求能成功接入。我建议把连接超时处理做成一个独立的状态机typedef enum { CONN_IDLE, CONN_ESTABLISHED, CONN_TIMEOUT, CONN_RELEASING } conn_state_t;连接处于CONN_ESTABLISHED时每个RPI周期都在更新“最后活跃时间”。一旦超过设定的超时阈值状态切换到CONN_TIMEOUT清理连接相关的资源。这样才能保证PLC重启后重新建立连接不会因为旧连接资源没释放而报错。6.3 看门狗与异常恢复STM32工程里加看门狗是常规操作但EtherNet/IP协议栈跑到死循环的情况不常见更常见的是某个组件卡住导致任务调度异常。我给自己的项目做了两级看门狗硬件看门狗喂狗逻辑放在最高优先级的任务里防止系统级死机软件看门狗定期检查CIP任务和LWIP任务是否有心跳更新如果某个任务长时间没响应就主动复位对应模块软件看门狗的价值在于系统没死机但协议栈卡死了这种情况硬件看门狗检测不到。我的实现方法是每个RTOS任务加一个运行计数器监控任务周期性更新一旦发现某任务计数器长时间未变化就触发系统复位或重新初始化协议栈。6.4 关于CIP Sync想清楚再做EtherNet/IP有一个进阶功能叫CIP Sync基于IEEE 1588精确时间同步协议用于多轴运动控制、分布式数据采集等对时间同步要求很高的场景。很多设备厂商会遇到客户问“支不支持CIP Sync”但实际项目里大多数IO类从站设备根本不需要这个功能。STM32要实现CIP Sync比较复杂硬件上需要支持时间戳的以太网外设软件上还要处理好同步算法。目前STM32的标准外设库对IEEE 1588的支持力度有限我在项目里通常的做法是如果客户真的需要时间同步就外扩一颗支持1588的PHY芯片或者换用带TSN功能的处理器。如果在普通STM32上做CIP Sync成本高、收益低而且很容易在一致性测试上卡住。6.5 现场总线之外的提醒协议只是通信产品价值在数据最后分享一点体会EtherNet/IP协议本身解决的是通信问题但用户买你的设备最终要的是设备能干活。协议栈再漂亮如果应用层的启动时序、故障诊断、数据采集做得不好现场用起来还是会出各种问题。我做的第一个EtherNet/IP从站项目客户反馈“偶尔通信中断”分析发现是应用层有一个耗时超过100ms的EEPROM写入操作直接把RTOS任务调度卡住了。后来把EEPROM写入改成异步队列问题就消失了。这个经验对我后来的项目设计帮助很大无论协议栈处理得多好应用层不能有长时间阻塞操作这是嵌入式实时系统的铁律。7. 关于接口兼容性的一点延伸思考如果你做的是面向多种PLC品牌的产品建议把EtherNet/IP从站代码尽量抽象成一个独立的通信模块和应用逻辑之间用清晰的数据接口隔开。这样做的好处是后期要支持Profinet、EtherCAT、Modbus TCP时只需要替换通信模块应用层代码可以复用。我个人的习惯是定义一组统一的IO数据交换结构体不管底层跑什么协议应用层看到的都是同一组数据结构。这样可以控制单个协议栈的复杂度也能让产品线在支持多协议时更平滑。基于OpENer/NIO移植到STM32这件事难度不在代码量而在于你对CIP对象模型和连接管理的理解深度。把Forward Open、RPI、Assembly映射这几个核心机制吃透就已经掌握了EtherNet/IP从站开发的八成精髓。后续无非是边做边踩坑每个坑都会让你对协议的理解更深一层。如果这篇文章能帮你少熬几个通宵那就是值得的。有问题你也可以把抓包文件发给我我们一起讨论具体的协议交互细节。本文还有配套的精品资源点击获取