wifi support tsl

This commit is contained in:
gcw_4spBpAfv
2026-04-14 09:02:41 +08:00
parent ead2060ab3
commit 0ee970d8bd

View File

@@ -25,6 +25,39 @@ from logger_manager import logger_manager
from wifi import wifi_manager
def _calculate_tcp_ssl_password(device_id, iccid):
"""
与服务器 calculatePassword(deviceId, iccid) 一致:
hex(md5(hex(md5(deviceId)) + iccid))iccid 为空则不拼接。
"""
md5_device_hex = hashlib.md5(device_id.encode("utf-8")).hexdigest()
if iccid:
md5_device_hex = md5_device_hex + iccid
return hashlib.md5(md5_device_hex.encode("utf-8")).hexdigest()
def _wifi_tls_would_block(exc):
"""
非阻塞 TLS 下 recv/send 常抛出 WANT_READ / WANT_WRITE或等价文案
表示需等待对端/内核缓冲区,不是断线。
"""
try:
import ssl as _ssl
except ImportError:
_ssl = None
if _ssl is not None and isinstance(exc, _ssl.SSLError):
err = getattr(exc, "errno", None)
if err in (
getattr(_ssl, "SSL_ERROR_WANT_READ", 2),
getattr(_ssl, "SSL_ERROR_WANT_WRITE", 3),
):
return True
msg = str(exc).lower()
if "did not complete" in msg and ("read" in msg or "write" in msg):
return True
return False
class NetworkManager:
"""网络通信管理器(单例)"""
_instance = None
@@ -181,6 +214,16 @@ class NetworkManager:
def read_device_id(self):
"""从 /device_key 文件读取设备唯一 ID失败则使用默认值"""
def _set_password_for_device_id(device_id):
if getattr(config, "USE_TCP_SSL", False):
iccid = self.get_4g_mccid()
iccid = iccid if iccid else ""
print(f"iccid: {iccid}")
self._password = _calculate_tcp_ssl_password(device_id, iccid)
else:
self._password = device_id + "."
print(f"self._password: {self._password}")
try:
with open("/device_key", "r") as f:
device_id = f.read().strip()
@@ -188,7 +231,7 @@ class NetworkManager:
self.logger.debug(f"[INFO] 从 /device_key 读取到 DEVICE_ID: {device_id}")
# 设置内部状态
self._device_id = device_id
self._password = device_id + "."
_set_password_for_device_id(device_id)
return device_id
except Exception as e:
self.logger.error(f"[ERROR] 无法读取 /device_key: {e}")
@@ -196,7 +239,7 @@ class NetworkManager:
# 使用默认值
default_id = "DEFAULT_DEVICE_ID"
self._device_id = default_id
self._password = default_id + "."
_set_password_for_device_id(default_id)
return default_id
# ==================== WiFi 管理方法(委托给 wifi_manager====================
@@ -461,6 +504,31 @@ class NetworkManager:
except Exception:
return None
def _maybe_add_iccid_to_login(self, login_data):
"""
若应用目录下尚无 iccid 标记文件,则在登录包中增加 iccid 字段(值为当前卡号)。
标记文件仅在「本次登录携带了 iccid 且服务器返回登录成功」后创建,见 _create_iccid_marker_file。
Returns:
bool: 本次登录是否携带了 iccid即成功后需要创建标记文件
"""
marker_path = os.path.join(config.APP_DIR, "iccid")
if os.path.exists(marker_path):
return False
iccid_val = self.get_4g_mccid()
login_data["iccid"] = iccid_val if iccid_val is not None else ""
return True
def _create_iccid_marker_file(self):
"""登录成功且曾携带 iccid 后创建空标记文件,后续登录不再带 iccid。"""
marker_path = os.path.join(config.APP_DIR, "iccid")
if os.path.exists(marker_path):
return
try:
with open(marker_path, "w"):
pass
except Exception as e:
self.logger.warning(f"[NET] 创建 iccid 标记文件失败: {e}")
def _apply_session_force_4g(self):
"""锁定本次会话为 4G直到关机期间不再回切 WiFi"""
self._session_force_4g = True
@@ -653,24 +721,77 @@ class NetworkManager:
return self._connect_tcp_via_4g()
return False
def _wrap_wifi_tls(self, plain_sock, hostname):
"""
在已建立的 TCP socket 上做 TLSWiFi 走主机 ssl 库4G 仍用模组 AT+SSL
校验与 config.SSL_VERIFY_MODE、SSL_CERT_PATH 一致。
"""
import ssl
verify_mode = getattr(config, "SSL_VERIFY_MODE", 0)
cert_path = getattr(config, "SSL_CERT_PATH", None) or ""
ca_ok = verify_mode == 1 and cert_path and os.path.exists(cert_path)
cert_none = getattr(ssl, "CERT_NONE", 0)
cert_required = getattr(ssl, "CERT_REQUIRED", 2)
if hasattr(ssl, "SSLContext"):
try:
proto = getattr(ssl, "PROTOCOL_TLS_CLIENT", None)
if proto is None:
proto = getattr(ssl, "PROTOCOL_TLS", 0)
ctx = ssl.SSLContext(proto)
if ca_ok:
ctx.load_verify_locations(cafile=cert_path)
ctx.verify_mode = cert_required
ctx.check_hostname = True
else:
ctx.check_hostname = False
ctx.verify_mode = cert_none
return ctx.wrap_socket(plain_sock, server_hostname=hostname)
except Exception as e:
self.logger.warning(f"[WIFI-TCP] SSLContext.wrap_socket 失败,改用 wrap_socket: {e}")
if ca_ok:
try:
return ssl.wrap_socket(
plain_sock,
server_hostname=hostname,
cert_reqs=cert_required,
ca_certs=cert_path,
)
except TypeError:
return ssl.wrap_socket(plain_sock, cert_reqs=cert_required, ca_certs=cert_path)
try:
return ssl.wrap_socket(plain_sock, server_hostname=hostname, cert_reqs=cert_none)
except TypeError:
return ssl.wrap_socket(plain_sock, cert_reqs=cert_none)
def _connect_tcp_via_wifi(self):
"""通过WiFi建立TCP连接"""
"""通过WiFi建立TCP连接USE_TCP_SSL 时在 TCP 之上走 tls"""
try:
# 创建TCP socket
wifi_manager.wifi_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
wifi_manager.wifi_socket.settimeout(5.0) # 5秒超时
# 连接到服务器
addr_info = socket.getaddrinfo(config.SERVER_IP, config.SERVER_PORT,
socket.AF_INET, socket.SOCK_STREAM)[0]
use_ssl = getattr(config, "USE_TCP_SSL", False)
host = self._server_ip
port = getattr(config, "TCP_SSL_PORT", 443) if use_ssl else config.SERVER_PORT
addr_info = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM)[0]
wifi_manager.wifi_socket.connect(addr_info[-1])
if use_ssl:
wifi_manager.wifi_socket = self._wrap_wifi_tls(wifi_manager.wifi_socket, host)
# 设置非阻塞模式(用于接收数据)
wifi_manager.wifi_socket.setblocking(False)
# 加快消息发送
wifi_manager.wifi_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self._tcp_connected = True
if use_ssl:
self.logger.info("[WIFI-TCP] TLS 连接已建立")
else:
self.logger.info("[WIFI-TCP] TCP 连接已建立")
# 启动 WiFi 质量后台检测
@@ -721,6 +842,14 @@ class NetworkManager:
"""检查WiFi TCP连接是否仍然有效"""
if not wifi_manager.wifi_socket:
return False
# TLS(ssl.wrap_socket/SSLContext.wrap_socket) 后的 socket 往往不支持 MSG_PEEK/MSG_DONTWAIT。
# 这种情况下“主动探测”反而容易误报断线;让真正的 send/recv 去判定更稳。
try:
if getattr(config, "USE_TCP_SSL", False) or hasattr(wifi_manager.wifi_socket, "cipher"):
return True
except Exception:
# 任何探测异常都不应导致断线清理
return True
try:
# send(b"") 在很多实现里是 no-op无法可靠探测断线。
# 用非阻塞 peek 来判断若对端已关闭recv 会返回 b""。
@@ -728,6 +857,13 @@ class NetworkManager:
if data == b"":
raise OSError("wifi socket closed")
return True
except TypeError as e:
# 某些实现(尤其是 TLS socket不支持 flags 参数;不要误判断线
try:
self.logger.warning(f"[WIFI-TCP] conncheck flags unsupported (TypeError): {e}")
except Exception:
pass
return True
except BlockingIOError:
# 无数据可读但连接仍在EAGAIN
return True
@@ -861,7 +997,13 @@ class NetworkManager:
# 标准 socket 发送
total_sent = 0
while total_sent < len(data):
try:
sent = wifi_manager.wifi_socket.send(data[total_sent:])
except (BlockingIOError, OSError) as se:
if _wifi_tls_would_block(se):
time.sleep_ms(2)
continue
raise
if sent == 0:
# socket连接已断开
self.logger.warning(f"[WIFI-TCP] 发送失败socket已断开尝试 {attempt+1}/{max_retries}")
@@ -1011,6 +1153,8 @@ class NetworkManager:
# 无数据可读是正常的
return b""
except OSError as e:
if _wifi_tls_would_block(e):
return b""
# socket错误连接断开等
self.logger.warning(f"[WIFI-TCP] 接收数据失败: {e}")
@@ -1309,6 +1453,8 @@ class NetworkManager:
"vol": vol_val,
"vol_per": voltage_to_percent(vol_val)
}
iccid_pending_marker = self._maybe_add_iccid_to_login(login_data)
print(f"login_data: {login_data}")
# if not self.tcp_send_raw(self.make_packet(1, login_data)):
if not self.tcp_send_raw(self._netcore.make_packet(1, login_data)):
self._tcp_connected = False
@@ -1347,7 +1493,7 @@ class NetworkManager:
if self._network_type == "wifi":
data = self.receive_tcp_data_via_wifi(timeout_ms=5)
if data:
self.logger.info(f"[NET] 接收WiFi数据, {time.time()}")
# self.logger.info(f"[NET] 接收WiFi数据, {time.time()}")
wifi_manager.recv_buffer += data
while len(wifi_manager.recv_buffer) >= 12:
try:
@@ -1394,6 +1540,9 @@ class NetworkManager:
logged_in = True
last_heartbeat_ack_time = time.ticks_ms()
self.logger.info("登录成功")
if iccid_pending_marker:
self._create_iccid_marker_file()
iccid_pending_marker = False
# 检查 ota_pending.json
try: