1111time_sleep = 5 # 采集间隔(秒)
1212SOCKET_PORT = 9000 # 监听端口
1313BUFFER_SIZE = 4096 # 接收缓冲区(增大以处理更大数据包)
14- RECV_TIMEOUT = 10 # 单次接收超时时间(秒),缩短以快速检测网络问题
15- MAX_RETRY = 3 # 超时最大重试次数,超过则断开连接重连
14+ RECV_TIMEOUT = 15 # 单次接收超时时间(秒),给 IoT 设备足够的响应时间
15+ MAX_RETRY = 10 # 超时最大重试次数,IoT 设备低功耗/网络波动较频繁,放宽阈值
16+ SEND_TIMEOUT = 10 # 发送阶段超时时间(秒)
17+ CHUNK_TIMEOUT = 3 # 分片读取额外数据的超时(秒),缩短以避免阻塞主循环
1618
1719# M1设备查询指令(保持原样)
1820GET_MSG = b'\xaa O\x01 %F\x11 9\x8f \x0b \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \xb0 \xf8 \x93 \x11 dR\x00 7\x00 \x00 \x02 {"type":5,"status":1}\xff #END#'
@@ -164,27 +166,51 @@ def start(self):
164166 def _handle_client (self , conn , addr ):
165167 """处理单个客户端连接"""
166168 _log (f"New connection from { addr } " , 0 )
167-
169+
168170 conn .setsockopt (socket .SOL_SOCKET , socket .SO_KEEPALIVE , 1 )
169- conn .settimeout (RECV_TIMEOUT )
170-
171+ # 启用 TCP keepalive 参数,更快检测死连接
172+ try :
173+ if hasattr (socket , 'TCP_KEEPIDLE' ):
174+ conn .setsockopt (socket .IPPROTO_TCP , socket .TCP_KEEPIDLE , 60 ) # 60s 空闲开始探测
175+ if hasattr (socket , 'TCP_KEEPINTVL' ):
176+ conn .setsockopt (socket .IPPROTO_TCP , socket .TCP_KEEPINTVL , 10 ) # 每 10s 探测一次
177+ if hasattr (socket , 'TCP_KEEPCNT' ):
178+ conn .setsockopt (socket .IPPROTO_TCP , socket .TCP_KEEPCNT , 5 ) # 5 次失败判定断开
179+ except Exception :
180+ pass
181+
171182 consecutive_timeout = 0
172183 total_data_count = 0
173-
184+
174185 try :
175186 while self .running :
187+ # ---------- 阶段 1:发送查询指令 ----------
176188 try :
189+ conn .settimeout (SEND_TIMEOUT )
177190 _log (f"Client { addr } sending query..." , 3 )
178191 conn .sendall (GET_MSG )
179192 _log (f"Client { addr } query sent, waiting for response..." , 3 )
180-
193+ except (socket .timeout , OSError , ConnectionError ) as e :
194+ _log (f"Client { addr } send query failed: { e } " , 1 )
195+ # 发送失败直接计入超时,不给太多重试机会(通常意味着连接已断)
196+ consecutive_timeout += 2
197+ if consecutive_timeout >= MAX_RETRY :
198+ _log (f"Client { addr } consecutive send failures, closing connection" , 2 )
199+ break
200+ time .sleep (2 )
201+ continue
202+
203+ # ---------- 阶段 2:接收首包数据 ----------
204+ try :
205+ conn .settimeout (RECV_TIMEOUT )
181206 data = conn .recv (BUFFER_SIZE )
182-
207+
183208 if not data :
184209 _log (f"Client { addr } closed connection (empty recv)" , 0 )
185210 break
186-
187- conn .settimeout (2 )
211+
212+ # ---------- 阶段 3:接收剩余分片 ----------
213+ conn .settimeout (CHUNK_TIMEOUT )
188214 while True :
189215 try :
190216 chunk = conn .recv (BUFFER_SIZE )
@@ -193,41 +219,24 @@ def _handle_client(self, conn, addr):
193219 data += chunk
194220 except socket .timeout :
195221 break
196-
197- conn .settimeout (RECV_TIMEOUT )
198-
199- json_data = self ._parse_data (data )
200- if json_data :
201- self ._process_data (json_data , addr )
202- total_data_count += 1
203- consecutive_timeout = 0
204- else :
205- _log (f"Client { addr } received data but no valid JSON (len={ len (data )} )" , 1 )
206222
207- # 处理完数据后,检查亮度控制
208- brightness = get_current_brightness ()
209- if brightness >= 0 and data and len (data ) >= 23 :
210- try :
211- brightness_json = json .dumps ({"brightness" : brightness })
212- brightness_msg = data [:23 ] + b'\x00 \x18 \x00 \x00 \x02 ' + brightness_json .encode ('utf-8' ) + b'\xff #END#'
213- conn .sendall (brightness_msg )
214- _log (f"Sent brightness control: { brightness } to { addr } " , 3 )
215- except Exception as e :
216- _log (f"Brightness control error: { e } " , 1 )
217-
218- time .sleep (time_sleep )
219-
220223 except socket .timeout :
221224 consecutive_timeout += 1
222- _log (f"Client { addr } recv timeout ({ consecutive_timeout } /{ MAX_RETRY } ), data received: { total_data_count } " , 1 )
223-
225+ _log (
226+ f"Client { addr } recv timeout ({ consecutive_timeout } /{ MAX_RETRY } ), "
227+ f"data packets received: { total_data_count } " ,
228+ 1 if consecutive_timeout < MAX_RETRY // 2 else 2
229+ )
224230 if consecutive_timeout >= MAX_RETRY :
225- _log (f"Client { addr } max recv timeout reached ({ MAX_RETRY } ), closing connection" , 2 )
231+ _log (
232+ f"Client { addr } max recv timeout reached ({ MAX_RETRY } ), "
233+ f"closing connection (total packets: { total_data_count } )" ,
234+ 2
235+ )
226236 break
227-
228237 time .sleep (1 )
229238 continue
230-
239+
231240 except ConnectionResetError :
232241 _log (f"Client { addr } reset connection (ConnectionResetError)" , 1 )
233242 break
@@ -238,24 +247,50 @@ def _handle_client(self, conn, addr):
238247 _log (f"Client { addr } connection aborted" , 1 )
239248 break
240249 except OSError as e :
241- _log (f"Client { addr } OS error: { e } " , 2 )
250+ _log (f"Client { addr } OS error on recv : { e } " , 2 )
242251 break
243- except Exception as e :
244- _log (f"Client { addr } unexpected error: { e } " , 2 )
245- consecutive_timeout += 1
246- if consecutive_timeout >= MAX_RETRY :
247- break
248- time .sleep (1 )
249- continue
250-
252+
253+ # ---------- 阶段 4:解析与处理数据 ----------
254+ # 数据接收成功,重置超时计数器
255+ consecutive_timeout = 0
256+
257+ json_data = self ._parse_data (data )
258+ if json_data :
259+ self ._process_data (json_data , addr )
260+ total_data_count += 1
261+
262+ # 处理完数据后,检查亮度控制
263+ brightness = get_current_brightness ()
264+ if brightness >= 0 and data and len (data ) >= 23 :
265+ try :
266+ conn .settimeout (SEND_TIMEOUT )
267+ brightness_json = json .dumps ({"brightness" : brightness })
268+ brightness_msg = data [:23 ] + b'\x00 \x18 \x00 \x00 \x02 ' + brightness_json .encode ('utf-8' ) + b'\xff #END#'
269+ conn .sendall (brightness_msg )
270+ _log (f"Sent brightness control: { brightness } to { addr } " , 3 )
271+ except Exception as e :
272+ _log (f"Brightness control error: { e } " , 1 )
273+ else :
274+ _log (f"Client { addr } received data but no valid JSON (len={ len (data )} )" , 1 )
275+
276+ # 等待下一次采集
277+ try :
278+ conn .settimeout (None ) # sleep 期间不需要超时
279+ except Exception :
280+ pass
281+ time .sleep (time_sleep )
282+
251283 except Exception as e :
252284 _log (f"Client { addr } fatal error: { e } " , 2 )
253285 finally :
254286 try :
255287 conn .shutdown (socket .SHUT_RDWR )
256- except :
288+ except Exception :
289+ pass
290+ try :
291+ conn .close ()
292+ except Exception :
257293 pass
258- conn .close ()
259294 _log (f"Connection closed: { addr } , total data packets: { total_data_count } " , 0 )
260295
261296 def _parse_data (self , data ):
0 commit comments