Paho-MQTT断网后无法重连,socket.gaierror异常如何解决?
MQTT封装类网络断开后触发socket.gaierror异常的解决方法
问题描述
我实现了一个MQTT封装类,正常运行无问题,但网络断开后出现socket.gaierror: [Errno -3] Temporary failure in name resolution异常。当前使用paho-mqtt 1.6.1版本,已设置self.suppress_exceptions = True以阻止paho-mqtt抛出异常,但socket层仍报错,请问如何避免该类型异常?
封装类代码
from paho.mqtt.client import Client as mqttClient import paho.mqtt.client as paho from queue import Queue import time class mqtt_instance: def __init__(self, client_id, host, port, auth): #"mqttClient.CallbackAPIVersion.VERSION1" for paho-mqtt version 2.0.0 otherwise remove it self._client_id = client_id self._host = host self._port = port self.suppress_exceptions = True self.connected = False self._reconnect = True # Just as a placeholder in def self.do_nothing = None # This list is needed when client re-connects to broker to re-subscribe to topics self.topic_list = [] # setup queue to capture messages from topic subscription self.q = Queue() # instantiate client from paho.mqtt library #print('MQTT WRAPPER: Instantiating mqtt client...') #self.client = mqttClient(mqttClient.CallbackAPIVersion.VERSION1) self.client = mqttClient(client_id=self._client_id, userdata=None, protocol=paho.MQTTv5) # Auth if auth is not None: self._user = auth[0] #print('MQTT WRAPPER: user:', self._user) self._pass = auth[1] #print('MQTT WRAPPER: pass:', self._pass) # enable TLS for secure connection self.client.tls_set(tls_version=paho.ssl.PROTOCOL_TLS) # set username and password self.client.username_pw_set(self._user, self._pass) # register callbacks #print('MQTT WRAPPER: registering callbacks...') self.client.on_connect = self._on_connect self.client.on_disconnect = self._on_disconnect self.client.on_subscribe = self._on_subscribe self.client.on_publish = self._on_publish self.client.on_unsubscribe = self._on_unsubscribe self.client.on_message = self._on_message def _on_connect(self, client, userdata, flags, rc, properties=None): print('MQTT WRAPPER: connected') #print('MQTT WRAPPER: rc = {0}'.format(rc)) if rc == 0: #print('MQTT WRAPPER: MQTT connection established...') self.do_nothing = None else: #print('MQTT WRAPPER: MQTT connection failed...') self.do_nothing = None self.connected = True for topic in self.topic_list: self.client.subscribe(topic) def _on_reconnect(self): print('MQTT WRAPPER: reconected') self.connected = True def _on_disconnect(self, client, userdata, rc, properties=None): #print('MQTT WRAPPER: on disconnect') self.connected = False print('MQTT WRAPPER: disconnected!') self.client.reconnect() def begin(self): # connect the client to the mqtt broker while not self.check_internet_connection: time.sleep(30) #print('MQTT WRAPPER: establishing connection...') self.client.connect( host=self._host, port=self._port, keepalive=60 ) self.client.loop_start()
异常信息
File "/home/pi/venv-ts/lib/python3.12/site-packages/paho/mqtt/client.py", line 3685, in _create_socket_connection return socket.create_connection(addr, timeout=self._connect_timeout, source_address=source) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/socket.py", line 828, in create_connection for res in getaddrinfo(host, port, 0, SOCK_STREAM): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/socket.py", line 963, in getaddrinfo for res in _socket.getaddrinfo(host, port, family, type, proto, flags): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ socket.gaierror: [Errno -3] Temporary failure in name resolution
解决方法
1. 修正suppress_exceptions的设置位置
原代码仅给封装类设置了suppress_exceptions = True,并未传递给paho-mqtt客户端实例,需要把该属性绑定到客户端上:
# 在__init__方法中实例化client后添加 self.client.suppress_exceptions = True
2. 用paho自带的自动重连替代手动调用
paho-mqtt内置自动重连机制,可通过配置避免手动重连时的异常泄露。在__init__中添加自动重连参数:
# 配置自动重连的延迟范围,指数退避 self.client.reconnect_delay_set(min_delay=1, max_delay=60)
然后修改_on_disconnect方法,移除手动reconnect()调用:
def _on_disconnect(self, client, userdata, rc, properties=None): self.connected = False print('MQTT WRAPPER: disconnected!') # 无需手动调用重连,客户端会自动触发
3. 捕获DNS解析异常并实现退避重试(手动重连场景)
如果坚持手动重连,需要在_on_disconnect中捕获socket.gaierror并添加退避逻辑:
# 先在文件顶部导入socket模块 import socket def _on_disconnect(self, client, userdata, rc, properties=None): self.connected = False print('MQTT WRAPPER: disconnected!') retry_delay = 1 max_delay = 60 while self._reconnect: try: client.reconnect() break except socket.gaierror: print(f'MQTT WRAPPER: DNS解析失败,{retry_delay}秒后重试') time.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_delay) except Exception as e: print(f'MQTT WRAPPER: 重连失败: {str(e)}') time.sleep(retry_delay) retry_delay = min(retry_delay * 2, max_delay)
4. 修复begin方法的网络检查逻辑
原代码中check_internet_connection未定义,需要实现可靠的网络检测函数:
def check_internet_connection(self): try: socket.getaddrinfo(self._host, self._port) return True except socket.gaierror: return False
然后修改begin方法的调用:
def begin(self): while not self.check_internet_connection(): print('MQTT WRAPPER: 网络未连接,30秒后重试') time.sleep(30) self.client.connect( host=self._host, port=self._port, keepalive=60 ) self.client.loop_start()
内容的提问来源于stack exchange,提问作者stekicar
相关产品推荐
相关产品推荐

