news 2026/10/14 15:09:14

安卓设备通过S7协议采集西门子PLC全系列数据方案详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
安卓设备通过S7协议采集西门子PLC全系列数据方案详解

做工业物联网这一行,最绕不开的就是和西门子PLC打交道。我最近半年都在折腾安卓设备直接走以太网通讯采集PLC数据的项目,期间加了几个同行群,发现一个特别共性的需求:大家都在找能兼容西门子全系PLC的安卓通讯方案——S7-300、S7-400、S7-1200、S7-1500,甚至老旧的S7-200都想在一个APP里搞定。

市面上不是没有商业控件,但收费是真贵,而且很多还是底层封装的黑盒,出了问题只能找售后。我自己踩了不少坑之后,手头正好沉淀了一套自研的安卓APP源码,底层通讯基于S7协议族实现,可以直接编译运行。今天不写虚的,把协议拆解、架构设计、实操踩坑全部分享出来,同行可以直接拿去参考。

1. 项目背景与需求拆解

1.1 从现场HMI到安卓屏,为什么需要一套自研通讯方案

很多工厂现场的HMI(人机界面)要么是西门子原装屏,要么是第三方组态屏。原装屏稳定性没问题,但价格贵,而且远程监控、数据上云这些需求一出来,原装屏的扩展性就捉襟见肘了。第三方组态屏呢,虽然支持不少PLC,但安卓生态下的定制化需求——比如把设备状态直接推送到手机、大屏看板、边缘计算等——传统组态屏基本做不了。

安卓设备最大的优势是生态和算力。一台几百块的安卓工控屏,性能比传统HMI强出好几个档次,还能跑MQTT、Modbus网关、Web服务等其它服务。但问题在于,安卓本身没有原生的PLC通讯库,Java和Kotlin生态里能找到的开源方案屈指可数,很多还只支持三菱、Modbus,西门子的支持往往不完整。

我做这套方案的核心思路就一个:用原生Java Socket实现S7协议的完整通讯链路,把对PLC的读写封装成一个独立的通讯引擎,APP上层只关心点位映射和业务逻辑,不关心协议细节。

这里要强调一个背景问题:很多同行一上来就找商业控件,其实如果只做数据采集和点位读写,S7协议本身并不复杂,自己实现完全可行。难的是把各种型号的差异、TSAP参数、PDU协商这些细节处理好。这部分如果没经验,确实要走不少弯路。

1.2 通讯方案的选型路线:S7协议族和它的变体

西门子PLC的以太网通讯,核心是S7协议。但所谓“全系支持”,并不意味着所有型号都走同一条路,必须先把协议族梳理清楚:

S7-300和S7-400是经典的模块化PLC,走ISO-on-TCP,抓包能看到TPKT头加COTP头,然后在上面承载S7协议。这里有个关键参数叫TSAP,全称是Transport Service Access Point,相当于传输层的“门牌号”,不同的机架号和插槽号会对应不同的TSAP值。

S7-1200和S7-1500是新一代PLC,同样走S7协议,但是TSAP的默认值和300/400不一样,握手流程里对PDU长度的要求也更高。S7-1500对通讯并发量、报文规范都更严格,稍不规范就会报错。

最特殊的是S7-200。它原生走的是PPI协议,串口居多,以太网模块出现之后才有了用PPI协议跑在以太网帧里的方案。到了S7-200 SMart,又改成了类似S7-300的以太网通讯方式。所以要兼容S7-200,必须单独处理PPI协议,或者通过PLC侧的Modbus模式来曲线救国。

我的方案设计是这样的:主体实现标准S7协议,覆盖S7-300/400/1200/1500;对S7-200和S7-200 SMART,给出单独的分支处理,其中S7-200 SMART走标准S7,老款S7-200走PPI协议适配。这个分类思路决定了后续整个代码结构。

1.3 方案目标与技术边界

做了这么长时间,我给自己定的目标是:

  • 支持绝对寻址方式下的位读写和字节/字/双字读写,覆盖M区、DB区、I区、Q区。
  • 支持单个点位读取和批量读取,批量读取可以大幅提高采集效率。
  • 支持通讯超时、重连机制,满足现场长时间运行的要求。
  • 代码结构清晰,通讯层和业务层完全分离,方便移植到其他平台。

技术边界要讲清楚:这套方案是数据采集和点位控制级别的通讯,不是西门子编程软件级别的全功能覆盖。不要指望用它能下载程序、在线监控梯形图,那些要用专业工程软件。它能做的是把PLC里的数据读出来、写进去,这是绝大多数工业物联网项目的核心诉求。

2. S7通讯协议核心拆解

2.1 报文结构:TPKT、COTP和S7三层

很多人拿到S7协议的报文第一反应是“这什么玩意,看不懂”。其实把层次拆开就清楚了。S7协议走TCP时是标准的四层结构,应用层从底往上分别是TPKT、COTP、S7。

TPKT是4个字节的传输层封装,首个字节固定0x03,第二个字节固定0x00,后两个字节表示整个TPKT报文的总长度。COTP是ISO传输层协议,在S7通讯里主要有两类:一类是连接建立时的CR和CC报文,用于携带TSAP信息;另一类是数据传输时的DT报文,PFG格式,前两个字节固定0x02 0xF0,用于承载S7报文。S7层才是真正的西门子应用协议,头10个字节是固定的S7 Header,里面有协议ID(固定0x32)、ROSCTR类型、PDU引用号、参数长度和数据长度等字段。

我整理了一个S7报文关键字段表,方便对照抓包分析:

字段长度说明
协议ID1字节固定0x32,标识S7协议
ROSCTR1字节消息类型,0x01为Job,0x02为ACK,0x03为ACK-Data,0x07为用户数据
冗余2字节固定0x0000
PDU引用2字节请求响应匹配用,相当于序列号
参数长度2字节参数区的字节数
数据长度2字节数据区的字节数,注意是不包含参数区的

ROSCTR是最容易看错的地方。读请求和写请求都是Job消息,PLC返回的结果如果是确认响应,可能是ACK或ACK-Data。ACK-Data在PLC有数据返回时出现,比如读操作的结果。判断是否通讯成功,除了看TCP层是否收到响应,还要看S7层的返回值字段,0x00表示成功,0x01表示硬件错误,0x02表示对象不存在,0x03表示数据长度错误,0x05表示地址错误,0x06表示写保护。

2.2 握手环节:TSAP参数与PDU协商

连接PLC不是TCP一通就完事,还要过“三道关”:TPKT封装、COTP连接请求、S7协商通讯。前两步合起来就是握手,关键在TSAP。

TPKT加COTP连接请求(CR报文)会携带本地的TSAP和远程的TSAP。我用一个实际例子来说明:S7-300,机架0插槽2的情况下,本地TSAP常设为0x0100(对应本地端口),远程TSAP设为0x0102(机架号乘0x100加插槽号再加0x01)。S7-1200和S7-1500更简单,默认本地TSAP是0x0101,远程TSAP是0x0102或0x0101,但要注意不同固件版本有差异。S7-400的默认布局又不一样,插槽号往往在3或4。

TSAP填错的最典型现象是:TCP连接能建立,但COTP握手失败,PLC直接断开连接。排查的时候不要只盯着IP地址和端口号,TSAP才是真正的幕后黑手。

PDU协商是握手阶段另一个关键动作。S7协议里PDU(Protocol Data Unit)是单次通讯能传输的最大数据长度。PLC型号不同,最大PDU长度也不同,S7-300很多只有240字节,S7-1200一般能到960字节,S7-1500能到960甚至更高。客户端在协商请求里会带上自己期望的PDU长度,PLC会返回它允许的长度,实际使用的PDU长度取两者中的较小值。

如果忽略PDU协商,直接按960字节长度去请求S7-300的数据,PLC会直接拒收响应或者返回错误。我的经验是:协商阶段统一请求最大长度,然后以PLC响应中的长度为准,后续所有读写的PDU长度都不能超过这个值。这个值还决定了单次批量读取最多能读多少个字节,算一下很有帮助。

2.3 读写指令:功能码与参数区、数据区

S7协议的读指令功能码是0x04,写指令是0x05。以读为例,Job消息的参数区结构是:功能码(0x04)、Item计数(通常为1)、然后是Item内容。

一个Item包含规格号(0x12)、长度(0x0A)、寻址模式(0x10表示绝对寻址)、传输大小、DB号(仅DB区使用)和数据地址。传输大小代码有讲究:位是0x01,字节是0x02,字是0x04,双字是0x06。地址部分一共4个字节,包含面积区标识和偏移,面积区0x81是M区、0x84是DB区、0x80是I区、0x82是Q区。

这里有一个容易出错的细节:地址解析时,位地址如M10.0,需要把字节偏移和位偏移分别处理,实际报文里有一个专门的位序号字段。很多初写者用整型直接算地址,结果读出来数据总是不对,就是这个细节没处理好。

写指令的结构和读指令有差异。写请求的Item里不是完整的目标地址描述,而是分两部分:前面是寻址参数,后面跟的是要写的数据本身。写数据部分前两个字节表示数据长度,然后是数据内容,如果要写位或字节,数据部分还需要按字对齐,不足一个字的高位补0。这个补位逻辑如果漏掉,PLC会报数据长度错误。

2.4 S7-200的特殊处理:PPI的帧格式差异

为什么单独把S7-200拿出来说?因为它的通讯协议和S7-300/400/1200/1500完全不同。S7-200没有以太网口时用串口走PPI,数据帧格式是类似Modbus的RTU帧,里边承载的是PPI报文。S7-200 SMART虽然升级了以太网口,但通讯协议已经接近S7-300。

我做的方案里,S7-200的兼容分成两条路:

老款S7-200带以太网模块,模块本身做了PPI-over-IP的映射,这种情况下客户端可以模拟PPI协议,直接通过TCP发送PPI帧。PPI帧的格式比较简单:起始符0x68、长度、重复长度、目标地址、源地址、功能码、数据、校验和。这里的校验不是CRC,而是简单的累加和校验,按字节求和取低八位。

S7-200通过Modbus RTU从站方式接入:如果PLC侧程序启用了Modbus从站库,安卓端按Modbus TCP协议直接读写,这是绕开PPI最省事的方式。很多老工程师一听到S7-200就头疼,其实走Modbus往往是更稳定的路线。

3. 自研APP的架构设计与模块划分

3.1 通讯引擎层:连接池与读写线程模型

安卓APP里对接PLC,最忌讳的就是在UI线程里直接做Socket读写。我在设计通讯引擎时,把所有网络操作封装在独立的线程池里,UI层通过回调接口拿结果。

通讯引擎的核心是一个S7Client类,它负责:TCP连接、COTP握手、PDU协商、读请求、写请求、断开连接。连接失败或通讯超时时,内部自动触发重连逻辑,重连间隔可以配置,一般建议3到5秒。这个类本身是无状态的,可以被多个任务复用,但我实际测试下来,同一时间只跑一个连接实例是最稳的,多个实例同时连同一个PLC,PDU引用号匹配容易乱。

线程模型上,我用了一个专有读写线程加一个定时任务调度器。定时任务调度器按照PLC类型和轮询周期,决定当前周期需要发送哪些点位读取请求。读到的数据通过消息队列分发给UI层,而不是直接在回调里更新UI,避免频繁的界面刷新造成卡顿。

3.2 点位管理层:地址映射与配置结构

点位管理是这套APP最实用的部分。我在数据库里放了一张点位映射表,字段包括点位逻辑名、PLC型号、面积区类型(M、DB、I、Q)、DB块号、字节偏移、位偏移、数据类型(位、字节、字、双字、实数)。UI层绑定的是逻辑名,比如“一号泵温度”,底层根据映射关系换成S7协议的报文地址。

这种设计带来一个很大的好处:现场PLC地址变了,不用改APP代码,直接在配置界面里改点位映射表就行。比如原来温度点在DB1.DBW0,后来改到DB1.DBW2,界面上改一下地址,保存后生效。

点位配置还支持分组和启用开关。分组可以按工艺流程分,比如“预热段”“反应段”“冷却段”,每组独立设置轮询周期。启停开关用于临时摘除故障点位,不影响整体通讯链路。

3.3 数据展示层:绑定机制与实时刷新策略

数据展示层我用的是数据绑定机制,观察者模式,点位值变化时自动通知绑定的控件更新。实时刷新策略需要讲究一下:不是所有数据都要高频刷新。

我的做法是按变量类型分轮询周期:高速量(设备启停状态、报警信号)300到500毫秒刷一次;中速量(温度压力流量)1到2秒刷一次;低速量(累积量、统计数据)5到10秒刷一次。轮询周期分开后,通讯负载大幅下降,PLC侧的CPU占用也降下来了。实际测试里,一台S7-1200挂了120个点位,快慢周期混跑,CPU占用不到5%。

UI展示上还加了一个数据质量标记,每次轮询超时或失败时,点位状态置为“异常”,控件直接置灰或变成警示色,而不是显示上一次的旧值。这个细节很多商用组态都做得不够好,但对现场维护来说太重要了。

4. 实操过程与核心环节实现

4.1 环境准备与开发工具选择

开发环境无非是Android Studio,SDK版本建议API 26以上,用Java还是Kotlin都行。我自己技术栈偏Java,所以通讯层用Java写,上层的业务界面用Kotlin写,两者混编在安卓里没有问题。

调试工具强烈推荐准备两个:一个是网络抓包工具,用于查看TCP报文,确认识别TSAP、PDU协商这些参数;另一个是西门子官方或第三方的仿真PLC工具,用于在没有实体PLC时先跑通通讯逻辑。我前期用模拟器调试,后期到现场用真机验证,效率高很多。

测试阶段建议先用真实的S7-1200或S7-1500实体PLC,这两个型号最常用,而且协议层面的约束更多,能暴露的问题也最全面。S7-300和S7-400的老型号协议的容错空间稍大,能跑通1200/1500,基本跑这些也没问题。

4.2 连接PLC的完整流程与关键代码

整个连接流程分四步:TCP连接、COTP握手、S7协商、进入读写循环。我用Java代码把这四步串起来。

第一步建立TCP连接,设置超时时间,我一般设5000毫秒。超过这个时间PLC还没响应,直接判定连接失败,进入重连流程。

Socket socket = new Socket(); socket.connect(new InetSocketAddress(plcIp, 102), 5000); socket.setSoTimeout(5000);

第二步发送COTP连接请求,这里要构建TPKT和COTP CR报文。TPKT长度按实际报文长度算,COTP CR里携带本地TSAP和远程TSAP。不同型号的TSAP值我在前面章节已经讲了,这里给一个S7-300/400的通用计算方式:

// 本地TSAP:0x0100 + 机架号 * 0x0100 + 插槽号 int localTsap = 0x0100 + rack * 0x0100 + slot; // 远程TSAP:0x0100 + 机架号 * 0x0100 + 插槽号 int remoteTsap = 0x0100 + rack * 0x0100 + slot;

注意S7-1200和S7-1500不需要按机架插槽计算,固定用默认TSAP就可以。这一步踩坑最多,我在5.1节详细说。

第三步S7协商通讯,发送SetupCommunication请求,请求PDU长度为960字节,然后从响应中解析PLC返回的最大PDU长度,后续所有读写报文长度都不得超过这个值。

第四步就是读写了。我封装了一个读字节区域的方法,核心是构造0x04功能码的读取报文,代码片段如下:

private byte[] buildReadRequest(Area area, int dbNumber, int byteOffset, int bitOffset, int size, int pduLength) { ByteBuffer buf = ByteBuffer.allocate(31).order(ByteOrder.BIG_ENDIAN); // TPKT头 buf.put((byte)0x03); buf.put((byte)0x00); buf.putShort((short)31); // COTP头 buf.put((byte)0x02); buf.put((byte)0xF0); buf.putShort((short)0x8000); // S7头 buf.put((byte)0x32); buf.put((byte)0x01); buf.put((byte)0x00); buf.put((byte)0x00); buf.putShort((short)0x0001); buf.putShort((short)0x0000); buf.put((byte)0x00); buf.put((byte)0x08); buf.putShort((short)0x0000); // 参数区开始 buf.put((byte)0x04); // 功能码READ buf.put((byte)0x01); // Item数 buf.put((byte)0x12); // 规格号 buf.put((byte)0x0A); // 长度 buf.put((byte)0x10); // 寻址模式: 绝对寻址 buf.put((byte)size); // 传输大小 buf.putShort((short)task.dbNumber); // DB号 buf.put((byte)area.code); // 面积区 buf.put((byte)byteOffset); buf.put((byte)0x00); buf.put((byte)task.bitOffset); return buf.array(); }

这段代码里最需要注意的是字节偏移和位偏移的组装方式。很多开源的实现是直接用一个整型数表示地址,然后字节位都靠位运算,容易错。我这样分开传参,上层点位配置的时候清清楚楚,不会产生歧义。

4.3 点位读写与轮询调优

点位读取的核心是批量读取,一次请求读取多个连续地址区域,效率提升非常明显。比如轮询一组温度传感器,地址是连续的,我可以一次把所有字节读回来,解析后拆成多个点位值。这比一个一个点位请求快上十几倍。

轮询调度器我用的是优先级队列加时间轮思想。每个点位组记录自己的下次执行时间,调度器每次取时间最近的请求发送。请求放到通讯引擎的发送队列里,响应回来后解析并分发。这里的经验值是:轮询周期不要太激进,一般300毫秒是下限,再快的话PLC的通讯模块压力会变大,实测S7-1500能扛住,但S7-300老型号会变成处理不过来的状态。

写操作相对读要谨慎很多。我的设计里,写操作不参与轮询,只由用户主动触发,而且每次写之前会做数据类型校验和范围校验,防止把非法值写进PLC。对于关键控制类的写入,我还做了一层二次确认,需要用户30秒内二次点击,这个看起来烦,但能有效防止误操作。

5. 常见问题与排查技巧实录

5.1 连接建立阶段的典型故障

这一阶段列一个速查表,都是我现场遇到过的真实问题:

现象可能原因解决办法
TCP连接超时网络不通、PLC未开启以太网通讯先ping PLC的IP,再用PC端抓包软件确认102端口是否响应
TCP能连但立即断开TSAP参数错误按PLC型号核对本地和远程TSAP,S7-300/400重点检查机架号和插槽号,S7-1200/1500确认默认值
COTTP握手成功但S7协商失败PLC通讯资源被占用检查PLC在线连接数,有些型号允许的并发连接数有限,关掉无关连接再试
连接成功但重连后失败上一次连接未正常释放客户端断开时发送关闭报文,确保PLC端释放连接资源

TSAP问题是最隐蔽的,我遇到过一台S7-300设备,在车间里一台能连一台不能连,最后发现两台PLC的硬件组态里模块插槽位置不同,导致TSAP不同。所以做项目时,一定要搞清楚每台设备的实际组态,不能拿着一个默认值通吃所有设备。

5.2 读写异常与数据错误排查

读写数据出错是最烦人的,因为TCP连接明明好好的,读回来的数据却不对。这类问题大多出在报文组装和解析上。

读取返回值为空但连接正常,大概率是目标地址不存在。确认点位地址是否真的存在于PLC里,DB块是否存在,偏移是否超出数据块长度。如果PLC返回错误代码,0x05是地址错误,0x07是写保护,对照表格处理就好。

位和字节的解析错误也常见。S7协议里位地址在报文中有专门的位置,我在3.2节讲过。如果读回来的位值总是反的或者错位,去检查报文中的位偏移字段是否和组态里一致。字节偏移超过128时,要留意PDU长度限制,超过后就得分帧读取。

还有一个经典的坑是实数还原。PLC里的REAL类型是32位浮点,很多安卓通讯库里解析的时候大小端搞反了,导致读回来的温度变成天文数字。我的方案里统一用大端解析,收到字节后用ByteBuffer的order方法指定BIG_ENDIAN,然后再转float,确保和PLC侧保存方式一致。

5.3 通讯稳定性问题的工程处理

现场通讯最大的敌人是电磁干扰和网络抖动。我做过一套设备,在工控柜旁边放了变频器,变频器启动的瞬间,无线网络直接卡断,PLC通讯也出现间歇性超时。

安卓设备走有线网口的时候,问题相对少;走WiFi时,稳定性考验很大。我的建议是现场优先用工业级路由器的有线端口,WiFi只用于调试。如果只能用WiFi,关闭安卓设备的WiFi省电模式,固定IP,不要用DHCP自动获取,减少网络重连的概率。

通讯层还要做断线重连和心跳检测。心跳机制我用的是周期性的读取某个固定点位的值,连续三次超时才判定连接断开,然后触发重连。不要用TCP上的ping或者Socket自带的keepalive,那些在工业环境下并不可靠,S7层面的心跳才是最实在的。

每次重连之后,还需要重新执行COTP握手和PDU协商,这个状态重置逻辑一定要做好。我见过不少开源方案,第一次连接没问题,PLC断电重启后客户端连不回来,就是状态机没有正确回到初始状态导致的。

6. 源码获取与后续扩展方向

6.1 源码结构一览与引入建议

这个APP源码我整理成了标准Android Studio工程,核心模块分了三层:core层放S7Client和协议构建解析;data层放点位配置管理和数据库;ui层放数据展示和配置界面。

可以直接在Android Studio里打开工程,改一下PLC的IP地址和点位配置就能跑起来。建议先连一台S7-1200或S7-1500,把基础的读取流程跑通,再逐步扩展其它功能。源码里我预留了日志开关,打开后可以打印完整的收发报文,方便排查问题。

引入自己的项目时,只需要把core层的S7Client拷过去就行,不依赖其它第三方库。这个设计是为了保证通讯层足够独立,方便移植到其它Java平台。

6.2 从S7通讯引擎到边缘网关的扩展思路

有了这套安卓端S7通讯引擎,能做的事其实很多。我自己的规划是往边缘网关方向扩展:在通讯引擎之上叠加MQTT模块,把采集到的点位数据按固定周期推送到云端平台;叠加本地存储模块,断网时数据先落SQLite,恢复后补传;叠加规则引擎,在本地做报警判断,阈值触发后直接推送消息。

更进阶的玩法是做协议转换:安卓设备充当一个协议网关,S7协议收到的数据,转成Modbus TCP协议暴露给其它系统。这样的话,老的S7-200也能融入新的物联网架构中。

另外想提醒一下,做这类工业项目,最好把采集的数据打上时间戳和质量戳,这个是和MES、SCADA对接时的硬性要求。我的源码里已经包含数据质量戳的实现,直接用就行。

最后说点实在的:我做这套方案的过程中,最大的体会是协议细节一定要吃透,尤其是PDU协商和TSAP这两个点。很多商业控件的稳定秘诀,说白了就是在这些容易错的地方做足了防御。如果你在集成过程中遇到问题,先抓包看报文,逐字段核对,八成问题都能定位。安卓采集西门子PLC这条路,只要把协议层打磨扎实,后面扩展什么功能都顺。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 15:08:54

高效跟进ArXiv CV论文:从筛选流程到OpenCV基础算子实践

我每天早上干的第一件事,不是看行情,也不是刷社媒,而是打开 ArXiv 的 CV 分类页,把这天新挂出来的计算机视觉预印本过一遍。这个习惯坚持了很多年,从最开始漫无目的地一篇篇点开,到现在形成了一套固定的筛选…

作者头像 李华
网站建设 2026/10/14 15:08:52

如何用打卡记录实现行为数据化?一套完整的打卡方法与复盘指南

2026年1月20日,一个看起来平平无奇的星期二。那天晚上我坐在书桌前,把手机里的打卡记录一项一项誊进表格,突然觉得这件事很有意思——这些零散的小勾、小数字,拼在一起居然能完整还原出一个人的一天。我那天总共记录了7项内容&…

作者头像 李华
网站建设 2026/10/14 15:08:37

地铁警戒线检测数据集实战:1125张YOLO+VOC双格式训练与避坑指南

简介:本资源为地铁警戒线检测数据集,面向从事目标检测算法训练与验证的开发者、学生及研究人员,可用于地铁站台安全监控场景下的警戒线识别模型训练与测试。压缩包共2000个文件,包含1125张jpg图片、1125个VOC格式xml标注文件及875…

作者头像 李华
网站建设 2026/10/14 15:08:27

工业视觉检测系统落地指南:从硬件选型到算法调优的避坑实战

1. 这块价值150亿美元的硬骨头,卡住了谁的脖子?工业视觉检测不是什么新鲜词。早在二十年前,生产线上的摄像头就已经在帮老板盯着产品有没有划痕、缺料、装歪。但这两年它突然从一个“质量辅助工具”升级成了“智造分水岭”,原因很…

作者头像 李华
网站建设 2026/10/14 15:08:27

BFS算法解决电梯最短路径问题与1-based索引实践

1. 项目背景与问题定义这个题目源自经典的"奇怪的电梯"编程问题,但加入了两个关键变化点:一是增加了测试样例的复杂度,二是刻意要求数据从数组下标1开始存储。这种设计在算法竞赛和实际工程中都有其特殊意义。我第一次遇到这个问题…

作者头像 李华
网站建设 2026/10/14 15:08:16

网络协议学习笔记:IP寻址、TCP/UDP、HTTP/HTTPS与DNS全解析

很多朋友问我,为什么网络协议学了又忘、忘了又学,明明 TCP 三次握手张口就来,IP 地址也会算,可一碰到真实故障还是不知道怎么下手。问题多半出在“记忆先于理解”:名词背熟了,背后的现实逻辑没串起来。我这…

作者头像 李华