做UDS诊断这些年我见过太多人在定时参数上栽跟头——尤其是P4Server和NRC 0x78这条线。平时大家看得最多的是P2、P2*、P3这些标准参数一旦遇到0x78很多人就直接懵了到底等多久CAN和以太网DoIP上是不是一个玩法今天这篇就把P4Server在CAN与以太网中的差异讲透顺便把NRC 0x78的处理技巧一次性说清楚。诊断工具开发、ECU标定、刷写流程调试的同学这篇对你绝对有参考价值。1. 先搞明白UDS定时参数体系里P4Server的位置1.1 从P2到P2*教科书里敢写但实操常记混的参数在ISO 14229-1里应用层的定时参数其实就那几条P2、P2*、P3、S3外加一个客户端侧的P2client/P2client。P2server_max默认是50ms意思是服务端ECU收到诊断请求后必须在50ms内开始向客户端Tester回响应。超过50ms怎么办标准给了条活路先回一个NRC 0x78Response Pending告诉客户端“我再处理一会儿”然后把P2定时器清零重跑这时限就放宽到P2server_max默认是5000ms。很多人在这一步就掉坑里了。0x78不是最终响应它只是“中间态”的占位帧。客户端收到0x78之后会把等待超时从P2切换到P2*也就是从50ms放宽到5秒。但这里有个容易被忽略的潜规则0x78不可以无限制地发。ISO 14229-1的本意是服务端在一轮请求-响应交互中通过0x78争取到的总时间不能超过P2server_max这个预算。换句话说从客户端发出请求那一刻算起到服务端给出最终响应正响应或非78的负响应整个链路的时间上限是P2server_max。那P4Server在哪儿严格讲ISO 14229-1标准正文里并没有一个叫“P4Server”的官方参数。但在真实工程项目中很多UDS协议栈尤其是bootloader刷写类、OEM定制类栈会单独定义一个参数用来控制“0x78挂起窗口”的总体时长。这个参数在不同厂商的代码里叫法五花八门有的叫P4Server有的叫PendingTimeout有的直接叫TotalResponseTime。我习惯把它统一理解为服务端允许通过0x78机制把最终响应拖到多晚的总预算。1.2 P4Server为什么值得单独拎出来讲有人会问既然P2*Server已经限时5秒为什么还要单独搞一个P4Server这个问题问到点子上了。一是因为P2是标准兜底值但实际刷写场景里某些ECU擦除整块Flash的时间可能超过5秒。OEM会针对特定服务定义更长的窗口比如GWM、BYD等主机厂的刷写规范里0x31 Routine Control的擦除操作经常允许30秒甚至更久。这个“更久”不可能靠改全局P2实现只能靠一个独立的挂起预算参数去控制。二是CAN和以太网两条物理链路的时序特征完全不同。CAN走ISO-TP分帧一帧最多8字节经典CAN或64字节CAN FD传个上千字节的响应得先发First Frame再协商Flow Control再吐Consecutive Frame整套交互本身就要消耗毫秒级甚至更久的时间。而DoIP走TCP/IP以太网MTU 1500字节单个UDS报文往往两三段就发完瓶颈跑到Nagle算法、延迟ACK、TCP拥塞这些网络层问题上去了。两种链路上0x78的发送时机、频率、以及P4Server的合理取值天然就不一样。所以在工程配置里P4Server通常不是一个单一常量而是跟传输层绑定的一组值。CAN版栈里P4Server可能设成6秒DoIP版栈里可能设成5秒额外网络余量甚至同一个ECU在两套总线下的启动刷写时间表都不一样。理解这个背景后面聊差异才有意义。2. CAN和以太网上P4Server的差异到底在哪2.1 物理链路层决定了“第一帧响应”的底噪先看最底层的时延构成。经典CAN总线500kbps是乘用车最常用的波特率一帧标准数据帧大概要占用200~260微秒的线缆时间。UDS请求从Tester发出经过CAN控制器、收发器、总线仲裁、ECU侧接收这个“请求到达”的物理传输时间通常在亚毫秒级。看起来很快但CAN是半双工广播总线一旦总线上有高优先级报文在跑诊断帧就得等总线空闲这个等待时间在总线负载高的时候可以膨胀到几毫秒甚至十几毫秒。以太网这边100BASE-TX的物理传输时间对1500字节的帧大概是120微秒比CAN还快但TCP/IP协议栈的处理开销、中断调度、Socket缓冲区拷贝会把端到端时延拉到几毫秒量级。如果测试工具或ECU侧没做合理的网络调优比如没开TCP_NODELAYNagle算法会把小的UDS帧攒在缓冲区里等确认额外增加40ms级的延迟。这对P4Server的意义在于服务端从“收到请求”到“发出0x78”的这一段本质上由协议栈处理和调度决定物理链路本身不是瓶颈。但链路差异会体现在“0x78帧真正到达客户端”的时间抖动量上。CAN下这个抖动小帧到达时间基本可预测DoIP下这个抖动大尤其在做车机级诊断时跟其他以太网流量混跑抖动可能翻倍。所以做DoIP诊断栈的P4预算得比CAN多留20%~30%的余量否则在高负载网络环境下容易误判超时。2.2 ISO-TP分帧和DoIP封装对“挂起窗口”的影响CAN上的UDS靠ISO 15765-2ISO-TP做传输层。一个超过7字节的响应要拆帧First FrameFF带总长度ECU回Flow ControlFC协商块大小和STmin然后Tester连续发Consecutive FrameCF。这里有个很实际的问题如果Tester侧接收逻辑处理不当FF发出去了ECU的FC回得慢或者STmin设置得太大整个响应会在传输层被拖慢。0x78在这种场景下有个特殊价值它本身只有3字节0x7F SID 0x78加上PCI单帧头也就4字节永远能塞进一个CAN帧里。所以哪怕总线再忙0x78也能快速发出去不会因为分帧协商而卡住。这也是为什么标准把0x78设计成“轻量级占位”——它必须低成本、低延迟地到达客户端。DoIP这边UDS报文外面套了8字节DoIP头部协议版本、版本反码、负载类型、负载长度再加上源地址和目标地址各2字节一共12字节额外开销然后装进TCP段。同样发一个0x78从UDS角度看是3字节从IP网络角度看是15字节应用负载外加TCP/IP头。单看这个开销差异其实很小真正影响P4Server配置的是TCP的传输行为。实际项目中我踩过一个坑DoIP客户端收0x78没问题但服务端在发送一系列Consecutive帧比如25KB的0x36 TransferData响应时中间夹了一个0x78因为TCP的ACK机制客户端把那一段响应按序接收没问题但应用层解析时错把0x78当成了完整响应导致状态机跳飞。这个问题的根因不是时序预算而是实现里没区分“传输层帧边界”和“应用层响应边界”。在DoIP上一个UDS响应可能被拆成多个TCP段0x78和最终响应可能在同一个TCP流里挨得很近解析时必须按DoIP负载长度字段去切帧不能想当然地“每收到一段就是一个完整响应”。2.3 DoIP连接管理对P4Server配置的连带影响再说一个DoIP特有的点连接生命周期。CAN诊断的会话是“总线上的请求-响应”没有连接概念DoIP则必须先建立TCP连接端口13400完成Routing Activation然后才能走诊断报文。这个机制带来的后果是客户端在等待0x78挂起期间如果TCP连接断了整个诊断会话就废了。因此在配置P4Server时DoIP侧通常还会考虑一个“连接保活窗口”。常见的做法是客户端在收到0x78后要么被动等待P4预算耗尽要么在预算过半时主动发Tester Present0x3E确认连接还活着。这里有个取舍——0x3E本身也是一个诊断请求服务端收到后也要回响应如果服务端正在忙一个长耗时内部操作可能连0x78都来不及回这时候客户端如果死等P4又恰好遇到TCP KeepAlive超时连接就会被操作系统回收。我建议的DoIP配置方案是P4Server预算内保留一个“连接健康检查”子周期具体做法是客户端每1秒检查一次Socket状态如果连续2次探测异常直接放弃本轮请求并重建连接而不是傻等满预算再超时。CAN侧没有这个烦恼总线不关帧就能到所以CAN侧的P4Server实现可以做得更简单、更“无脑”。2.4 两条链路的P4Server参数参考值不同OEM和栈的实现不一样但基于我接触过的量产项目可以给一组实际工程里比较稳的参考值。注意这些不是ISO强制值是“常见实践”层面的经验值。配置项CANISO-TP以太网DoIPP2Server50ms50msP2*Server5000ms5000msP4Server挂起总预算6000ms预留ISO-TP重传余量5000ms网络抖动大时可调至7000ms0x78发送间隔50~200ms100~500ms避免TCP小包风暴最终响应超时P4Server 100msP4Server 500msTCP段重组余量连接保活检查不需要每1s探测Socket状态为什么CAN侧P4Server反而要给得比以太网多因为ISO-TP重传机制会吃掉时间。如果接收方没收到Consecutive Frame或者Flow Control丢了发送方要等待N_As/N_Ar超时再重传这些传输层重试都会算进挂起窗口的总时长。而DoIP的TCP协议栈自己负责可靠传输和重传应用层不用额外预留这部分余量。3. NRC 0x78的完整处理流程与避坑指南3.1 服务端什么时候该发0x78服务端发0x78的原则一句话判断自己能否在P2server_max50ms内给出最终响应不能就立刻发0x78。这里有个细节值得强调——标准要求的是“在P2时间内开始发送响应”不是“在P2时间内发送完”。对于多帧响应你只要在50ms内发出第一个帧对CAN来说是FF或SF对DoIP来说是第一个TCP段就不算违约。所以一个常见的实现误区是ECU处理函数耗时30ms但生成的总响应有几百字节开发人员怕超时一上来就发0x78。其实完全没必要。只要在50ms内发出第一帧后续帧按正常节奏发就行。0x78只在“第一帧都没法按时发出”时才需要。另一个容易搞错的点0x78应当只针对物理寻址的请求。功能寻址Functional Addressing下一个请求广播给多个ECU如果每个ECU都回0x78总线会瞬间被占位帧淹没客户端也无法判断是谁在挂起。ISO 14229-1对功能寻址的响应有明确约束但工程上更稳妥的做法是功能寻址的请求要么在P2内直接回正响应要么不回响应取决于服务定义和抑制位不要发0x78。3.2 客户端收到0x78之后的正确姿势客户端侧的处理核心是“重置等待定时器但不重置总预算”。很多代码写得不对典型例子收到0x78后直接把定时器重设为5秒然后机械地循环等待完全不记总时长。结果服务端因为bug在无限发0x78客户端就永远等下去测试用例卡死只能手动断开。正确逻辑分三步。第一步识别帧类型响应里的第二个字节是0x7F第三个字节是0x78对应请求SID确认这是Response Pending第二步重置短期等待超时为P2*5秒这样服务端如果后续一直没有帧过来客户端能在5秒后判超时第三步检查总预算从原始请求发出到此刻的累计时间不能超过P4Server超了就强制终止等待并报错。这里还要注意一个边角情况0x78里带的SID必须是原始请求的SID。有时候协议栈实现得随意0x78里的SID写错了客户端按SID做状态分发就会挂起。做客户端解析时务必校验“0x78的SID 当前等待中的请求SID”不一致直接按协议异常处理。3.3 服务端发0x78的节奏控制服务端发0x78不是越快越好。每次发0x78都会重置客户端的P2*计时器如果发得太频繁比如每1ms发一个0x78虽然不会违约但会把总线或网络带宽白白耗掉客户端日志也会被刷屏回头排查问题根本看不清有效信息。我见过比较规范的实现是服务端发0x78的间隔取50ms到100ms并且用一个独立的“0x78重复计数器”做保护。连续发了比如20次0x78还没准备好就不要继续空转了要么强制给出最终响应哪怕是负响应要么进入错误处理流程。这里也呼应了P4Server的作用如果用P4Server限定了总预算这个循环就必须在预算内跳出否则就是协议栈实现有严重bug。另外一个细节是0x78发出后如果服务端在某个瞬间已经准备好了最终响应应该立刻发不要等到下一个0x78的发送节拍。因为客户端在P2*窗口内任何时刻收到最终响应都会正常接受没必要卡节拍反而增加响应延迟。用中断或标志位去打断0x78发送循环比轮询更可靠。3.4 刷写场景里0x78的多发与总预算控制刷写Flashing是0x78最密集的场景。0x34 RequestDownload、0x36 TransferData、0x37 RequestTransferExit这些服务都经常触发0x78因为Flash擦写操作动辄几百毫秒到几秒。刷写场景有个特殊之处Tester每次发一个0x36ECU擦写一个block然后回正响应或0x78。一个block的擦写时间如果不稳定0x78的次数就不固定。这时候P4Server按“单次请求”来算还是按“整个刷写过程”来算答案是一次请求一次算。每个0x36请求的响应挂起预算都是独立的不能把整个刷写过程的时长累加到一个P4Server里。这一点在做刷写时序测试时特别容易混淆有些测试脚本把整个刷写总时长跟P4Server对比得出“超时”的错误结论原因就是搞混了粒度。4. 实战代码示例P4Server配置与0x78处理4.1 协议栈中P4Server的配置结构体下面这段C代码展示了一个典型的UDS协议栈配置结构体其中包含P4Server以及相关定时参数。注意我把P4Server拆成了两个字段一个是总预算一个是0x78发送间隔这样便于在不同总线类型下独立调整。typedef enum { UDS_TRANSPORT_CAN, /* ISO-TP over CAN */ UDS_TRANSPORT_CANFD, /* ISO-TP over CAN FD */ UDS_TRANSPORT_DOIP /* DoIP over Ethernet */ } UdsTransportType; typedef struct { UdsTransportType transport; uint32_t p2_server_max_ms; /* 默认 50 */ uint32_t p2star_server_max_ms; /* 默认 5000 */ uint32_t p4_server_max_ms; /* 0x78挂起总预算 */ uint32_t p4_server_interval_ms; /* 相邻0x78最小间隔 */ uint32_t s3_server_max_ms; /* 会话超时默认5000 */ uint16_t max_pending_retries; /* 0x78最大连续次数 */ } UdsServerConfig; const UdsServerConfig UDS_CFG_CAN { .transport UDS_TRANSPORT_CAN, .p2_server_max_ms 50, .p2star_server_max_ms 5000, .p4_server_max_ms 6000, .p4_server_interval_ms 50, .s3_server_max_ms 5000, .max_pending_retries 60, }; const UdsServerConfig UDS_CFG_DOIP { .transport UDS_TRANSPORT_DOIP, .p2_server_max_ms 50, .p2star_server_max_ms 5000, .p4_server_max_ms 5000, .p4_server_interval_ms 100, .s3_server_max_ms 5000, .max_pending_retries 20, };为什么DoIP的max_pending_retries比CAN少因为DoIP下一帧的成本TCP/IP协议栈处理、网络调度比CAN高发太多0x78反而容易触发TCP小包堆积影响最终响应帧的发送时机。50ms间隔发20次也就是最多延后2秒配合P4Server的5秒预算足够覆盖绝大多数长耗时操作。4.2 服务端0x78发送逻辑示例服务端处理请求时判断无法在P2内响应就进入0x78流程。核心点是“检查预算、检查间隔、防死循环”。static UdsStatus UdsServer_ProcessRequest( const UdsServerConfig* cfg, const uint8_t* request, uint16_t req_len, uint8_t* response, uint16_t* resp_len) { uint32_t elapsed_ms 0; uint16_t pending_cnt 0; uint32_t tick_ms; if (UdsServer_IsReadyNow()) { return UdsServer_BuildFinalResponse(...); } tick_ms UdsServer_GetTick(); while (elapsed_ms cfg-p4_server_max_ms) { if (pending_cnt cfg-max_pending_retries) { /* 防止0x78无限循环直接回GeneralReject */ response[0] 0x7F; response[1] request[0]; response[2] NRC_GENERAL_REJECT; *resp_len 3; return UDS_STATUS_NOK; } UdsServer_SendFrame(cfg, response, 3, request[0], NRC_RESPONSE_PENDING); pending_cnt; /* 等一个0x78间隔期间检查是否准备完成 */ while ((UdsServer_GetTick() - tick_ms) cfg-p4_server_interval_ms) { if (UdsServer_IsReadyNow()) { return UdsServer_BuildFinalResponse(...); } } elapsed_ms UdsServer_GetTick() - tick_ms; } /* 预算耗尽必须给最终响应 */ response[0] 0x7F; response[1] request[0]; response[2] NRC_GENERAL_REJECT; *resp_len 3; return UDS_STATUS_NOK; }这段代码里有个容易被忽略的细节UdsServer_SendFrame发送0x78的时候我把response数组的前三个字节组装好但这个函数内部必须确保“单帧发送”在CAN下用SingleFrame格式在DoIP下用DoIP诊断报文格式。尤其DoIP下0x78的DoIP payload是8字节头4字节地址3字节数据一个都不能少否则客户端会解析失败。4.3 客户端处理0x78的Python参考实现客户端侧用Python做个DoIP的UDS请求函数方便测试脚本直接复用。这里的关键是“短期超时”和“总预算”分开管。import socket import struct import time class UdsTimeoutError(Exception): pass def uds_request_doip(sock, req_sid, req_data, p2_timeout0.05, p2star_timeout5.0, p4_timeout5.5): sock: 已激活Routing的DoIP TCP socket 返回最终响应字节自动处理NRC 0x78 doip_msg b\x03\xfc struct.pack(H, 0x8001) \ struct.pack(I, 4 len(req_data)) \ struct.pack(HH, 0x0E00, 0x0001) \ bytes([req_sid]) bytes(req_data) sock.settimeout(p2_timeout) sock.sendall(doip_msg) start time.monotonic() while True: try: resp recv_doip_message(sock) except socket.timeout: raise UdsTimeoutError( ftimeout after {time.monotonic()-start:.3f}s) # 检查总预算P4Server elapsed time.monotonic() - start if elapsed p4_timeout: raise UdsTimeoutError( fexceed p4 budget {elapsed:.3f}s) # 解析UDS响应: [SID0x40] 或 [0x7F][SID][NRC] if resp[0] 0x7F and resp[2] 0x78: # 0x78重置短期超时为P2* sock.settimeout(p2star_timeout) continue # 最终响应 return resp注意我设置的p4_timeout是5.5秒稍大于P2*的5秒但小于CAN版配置的6秒。原因是DoIP下TCP协议栈本身会保证数据不丢不需要像CAN那样预留ISO-TP重传余量。如果客户端在5.5秒还没等到最终响应那大概率是服务端状态机出问题了继续等没有意义。还有一个Python实现里的细节recv_doip_message必须按DoIP头部的payload长度字段循环读取完整报文不能靠一次recv就认为拿到了完整数据。TCP是字节流一个UDS响应可能在多个TCP段里也可能多个小报文挤在一个TCP段里。这个函数写不对0x78处理得再对也没用。5. 常见问题与排查技巧实录5.1 我实际踩过的坑第一个坑是Nagle算法干掉了我的一整个测试序列。当时用自研DoIP工具刷写0x36请求发出后始终等不到ECU的0x78响应超时一报一个准。排查半天发现是TCP_NODELAY没开小报文被Nagle算法攒住等到ECU的ACK回来才一起发时序完全乱套。解决办法很简单客户端Socket设置TCP_NODELAY服务端同样要设置两端都开了才顺畅。第二个坑是ISO-TP的Flow Control超时被误判成0x78超时。CAN刷写时ECU回Flow Control的时机受CAN总线调度影响有几次延迟超过预期Tester误以为ECU卡死直接终止了刷写。后来把传输层的N_Bs/N_Br超时和UDS应用层的P4Server预算分开记录日志里才能清楚区分到底是哪层超时问题才暴露出来并解决。第三个坑是关于0x78里SID写错的。有个供应商的协议栈在响应用0x36服务时0x78里带的SID写成了0x34的客户端按SID做多任务分发直接出错。这种问题纯靠功能测试很难发现建议做诊断时序自动化测试时加一条“0x78帧内容校验”用例把SID一致性检查常态化。5.2 问题排查速查表现象可能原因排查方法CAN下收到0x78后客户端仍在50ms超时客户端没从P2切换到P2*计时检查客户端状态机收到0x78必须重置超时为P2*0x78收到多次后客户端放弃等待总预算P4Server设太短按链路类型区分P4ServerCAN预留ISO-TP重传余量DoIP刷写时响应延迟忽高忽低TCP_NODELAY未开启在客户端与服务端均设置TCP_NODELAY服务端一直发0x78不停0x78重试次数无上限配置max_pending_retries预算耗尽强制回NRC0x78里的SID和请求SID不一致协议栈实现bug加SID一致性校验用例功能寻址请求导致多ECU回0x78协议使用不当功能寻址下禁止服务端发0x78ISO-TP多帧响应乱序/丢帧Flow Control或STmin配置不当检查N_Bs/N_Br超时参数调大STminDoIP连接在挂起期间断开TCP KeepAlive超时客户端1秒探测Socket状态超时重建连接5.3 几条压箱底的工程建议做诊断测试脚本时永远给UDS请求加一个“最大总时长”参数不要把超时寄托在某个具体TCP或者Socket读超时上。这样不管链路怎么变测试不会无限挂起定位问题时还能从日志里直接看到耗时分布。做ECU端0x78逻辑时多在“发送间隔”和“最大次数”上下功夫不要只盯P4Server这一个字段。实际项目中200ms一次0x78、最多发10次这个组合比50ms一次、最多发60次更稳因为在总预算接近耗尽时低频发送能显著降低总线上占位帧的比例给最终响应帧留出更干净的发送窗口。我个人在实际操作中最深的体会是UDS定时参数这东西标准给的是底线工程给的是余量。P4Server这样的参数在不同栈里叫法不一样、默认值不一样但本质都是“给0x78机制上一个总闸”。你把总闸的逻辑理清楚在CAN和DoIP两条链路上做好区分很多莫名其妙的超时问题都能迎刃而解。真要在量产项目里遇到疑难时序记得把客户端、服务端两层日志按时间戳对齐不要从单端去猜那才是最高效的排查思路。
企业数字化 ERP 产品动态
相关推荐
基于ESP32-S3与SCPI的台式电源可编程改造方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:32:37
USB插入自动切换电路设计:PMOS+肖特基实现锂电池与USB供电无缝切换 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:32:37
基于SpringBoot的校园二手交易平台:数据库设计与核心接口实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:32:31
PHP 自动化请求与模拟登录:不写刷赞工具也能练透这些技术 这类主题我不能帮你写。标题里的“一键领取名片赞”“一键领取圈圈赞”,本质上是一个自动刷赞、批量互动的小工具。这类工具不管代码写得怎么样,落到实际用途就是批量制造虚假互动、绕过平台风控,属于平台规则明令禁止的作弊行为。作为博主我… · 2026/9/25 7:56:24
酒店智能客房设备和服务响应系统如何管理,如何选择 截至 2026 年 9 月,越来越多酒店在做智能化升级时发现一个尴尬:灯光、空调、窗帘装了智能控制,客需呼叫上了小程序,影音娱乐又是另一套——设备是"智能"了,管理却更碎了。客房设备一套系统、服务响应一套系… · 2026/9/25 7:56:24
PHP对接EOS区块链:PHP开发包实现RPC调用与离线签名实战 很多人第一次看到“php <<<eos”这个标题,第一反应是PHP里的heredoc字符串语法,第二反应才可能是EOS区块链。两个理解其实都对,这个项目的核心就是用PHP通过开发包对接EOS区块链——而<<<eos那种“向EOS输出一段内容”的语… · 2026/9/25 7:56:24
广氟 PTFE 全矩阵方案:解决半导体 / 算力 / 新能源高端工况痛点 高端制造卡脖子痛点:PTFE 膜细分品类的现实供需矛盾半导体、AI 算力、储能电池、高频通信快速扩张,下游不再只追求 “能用” 的 PTFE 材料。高速 PCB 需要极低介电损耗;半导体湿法制程过滤膜要兼顾耐强氧化剂与高精度截留;电池 PA… · 2026/9/25 7:56:24
浏览器自动化脚本开发指南:从篡改猴到用户脚本实战 1. 从“雨课堂刷课教程”这个标题说起“雨课堂刷课教程”这个标题,乍一看像是一份操作指南,但稍微有点开发经验的人都能嗅到它背后的技术气息——浏览器自动化。热搜词里那一串“篡改猴”“Tampermonkey”“脚本”“谷歌浏览器”已经把答案摆在了桌面上&… · 2026/9/25 7:56:24
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37