Added TCP client reconnection on TCP socket drop
This commit is contained in:
parent
6362e04567
commit
65e8487b39
@ -301,7 +301,10 @@ class AX25KISSInterface(Interface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "AX25KISSInterface["+self.name+"]"
|
return "AX25KISSInterface["+self.name+"]"
|
@ -277,7 +277,10 @@ class KISSInterface(Interface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "KISSInterface["+self.name+"]"
|
return "KISSInterface["+self.name+"]"
|
@ -127,6 +127,10 @@ class LocalClientInterface(Interface):
|
|||||||
if self in RNS.Transport.local_client_interfaces:
|
if self in RNS.Transport.local_client_interfaces:
|
||||||
RNS.Transport.local_client_interfaces.remove(self)
|
RNS.Transport.local_client_interfaces.remove(self)
|
||||||
|
|
||||||
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "LocalInterface["+str(self.target_port)+"]"
|
return "LocalInterface["+str(self.target_port)+"]"
|
||||||
|
@ -463,7 +463,10 @@ class RNodeInterface(Interface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "RNodeInterface["+self.name+"]"
|
return "RNodeInterface["+self.name+"]"
|
||||||
|
@ -130,7 +130,10 @@ class SerialInterface(Interface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("A serial port error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("The interface "+str(self.name)+" is now offline. Restart Reticulum to attempt reconnection.", RNS.LOG_ERROR)
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "SerialInterface["+self.name+"]"
|
return "SerialInterface["+self.name+"]"
|
||||||
|
@ -23,13 +23,21 @@ class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class TCPClientInterface(Interface):
|
class TCPClientInterface(Interface):
|
||||||
|
RECONNECT_WAIT = 5
|
||||||
|
RECONNECT_MAX_TRIES = None
|
||||||
|
|
||||||
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None):
|
def __init__(self, owner, name, target_ip=None, target_port=None, connected_socket=None, max_reconnect_tries=None):
|
||||||
self.IN = True
|
self.IN = True
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.socket = None
|
self.socket = None
|
||||||
self.parent_interface = None
|
self.parent_interface = None
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.initiator = False
|
||||||
|
|
||||||
|
if max_reconnect_tries == None:
|
||||||
|
self.max_reconnect_tries = TCPClientInterface.RECONNECT_MAX_TRIES
|
||||||
|
else:
|
||||||
|
self.max_reconnect_tries = max_reconnect_tries
|
||||||
|
|
||||||
if connected_socket != None:
|
if connected_socket != None:
|
||||||
self.receives = True
|
self.receives = True
|
||||||
@ -50,11 +58,43 @@ class TCPClientInterface(Interface):
|
|||||||
self.writing = False
|
self.writing = False
|
||||||
|
|
||||||
if connected_socket == None:
|
if connected_socket == None:
|
||||||
|
self.initiator = True
|
||||||
thread = threading.Thread(target=self.read_loop)
|
thread = threading.Thread(target=self.read_loop)
|
||||||
thread.setDaemon(True)
|
thread.setDaemon(True)
|
||||||
thread.start()
|
thread.start()
|
||||||
self.wants_tunnel = True
|
self.wants_tunnel = True
|
||||||
|
|
||||||
|
def reconnect(self):
|
||||||
|
if self.initiator:
|
||||||
|
attempts = 0
|
||||||
|
while not self.online:
|
||||||
|
attempts += 1
|
||||||
|
|
||||||
|
if self.max_reconnect_tries != None and attempts > self.max_reconnect_tries:
|
||||||
|
RNS.log("Max reconnection attempts reached for "+str(self), RNS.LOG_ERROR)
|
||||||
|
self.teardown()
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.socket.connect((self.target_ip, self.target_port))
|
||||||
|
self.online = True
|
||||||
|
self.writing = False
|
||||||
|
|
||||||
|
thread = threading.Thread(target=self.read_loop)
|
||||||
|
thread.setDaemon(True)
|
||||||
|
thread.start()
|
||||||
|
RNS.Transport.synthesize_tunnel(self)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Reconnection attempt for "+str(self)+" failed. The contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
|
time.sleep(TCPClientInterface.RECONNECT_WAIT)
|
||||||
|
|
||||||
|
else:
|
||||||
|
RNS.log("Attempt to reconnect on a non-initiator TCP interface. This should not happen.", RNS.LOG_ERROR)
|
||||||
|
raise IOError("Attempt to reconnect on a non-initiator TCP interface")
|
||||||
|
|
||||||
def processIncoming(self, data):
|
def processIncoming(self, data):
|
||||||
self.owner.inbound(data, self)
|
self.owner.inbound(data, self)
|
||||||
|
|
||||||
@ -105,24 +145,30 @@ class TCPClientInterface(Interface):
|
|||||||
escape = False
|
escape = False
|
||||||
data_buffer = data_buffer+bytes([byte])
|
data_buffer = data_buffer+bytes([byte])
|
||||||
else:
|
else:
|
||||||
RNS.log("TCP socket for "+str(self)+" was closed, tearing down interface", RNS.LOG_VERBOSE)
|
RNS.log("TCP socket for "+str(self)+" was closed, attempting to reconnect...", RNS.LOG_WARNING)
|
||||||
self.teardown()
|
self.online = False
|
||||||
|
if self.initiator:
|
||||||
|
self.reconnect()
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.online = False
|
self.online = False
|
||||||
RNS.log("An interface error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
RNS.log("An interface error occurred, the contained exception was: "+str(e), RNS.LOG_ERROR)
|
||||||
RNS.log("Tearing down "+str(self), RNS.LOG_ERROR)
|
|
||||||
self.teardown()
|
self.teardown()
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
|
RNS.log("The interface "+str(self)+" experienced an unrecoverable error and is being torn down. Restart Reticulum to attempt to open this interface again.", RNS.LOG_ERROR)
|
||||||
self.online = False
|
self.online = False
|
||||||
self.OUT = False
|
self.OUT = False
|
||||||
self.IN = False
|
self.IN = False
|
||||||
if self in RNS.Transport.interfaces:
|
if self in RNS.Transport.interfaces:
|
||||||
RNS.Transport.interfaces.remove(self)
|
RNS.Transport.interfaces.remove(self)
|
||||||
|
|
||||||
|
if RNS.Reticulum.panic_on_interface_error:
|
||||||
|
RNS.panic()
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "TCPInterface["+str(self.name)+"/"+str(self.target_ip)+":"+str(self.target_port)+"]"
|
return "TCPInterface["+str(self.name)+"/"+str(self.target_ip)+":"+str(self.target_port)+"]"
|
||||||
|
@ -98,6 +98,8 @@ class Reticulum:
|
|||||||
Reticulum.__transport_enabled = False
|
Reticulum.__transport_enabled = False
|
||||||
Reticulum.__use_implicit_proof = True
|
Reticulum.__use_implicit_proof = True
|
||||||
|
|
||||||
|
Reticulum.panic_on_interface_error = False
|
||||||
|
|
||||||
self.local_interface_port = 37428
|
self.local_interface_port = 37428
|
||||||
self.share_instance = True
|
self.share_instance = True
|
||||||
|
|
||||||
@ -195,6 +197,10 @@ class Reticulum:
|
|||||||
v = self.config["reticulum"].as_bool(option)
|
v = self.config["reticulum"].as_bool(option)
|
||||||
if v == True:
|
if v == True:
|
||||||
Reticulum.__transport_enabled = True
|
Reticulum.__transport_enabled = True
|
||||||
|
if option == "panic_on_interface_error":
|
||||||
|
v = self.config["reticulum"].as_bool(option)
|
||||||
|
if v == True:
|
||||||
|
Reticulum.panic_on_interface_error = True
|
||||||
if option == "use_implicit_proof":
|
if option == "use_implicit_proof":
|
||||||
v = self.config["reticulum"].as_bool(option)
|
v = self.config["reticulum"].as_bool(option)
|
||||||
if v == True:
|
if v == True:
|
||||||
@ -510,6 +516,14 @@ share_instance = Yes
|
|||||||
|
|
||||||
shared_instance_port = 37428
|
shared_instance_port = 37428
|
||||||
|
|
||||||
|
# You can configure Reticulum to panic and forcibly close
|
||||||
|
# if an unrecoverable interface error occurs, such as the
|
||||||
|
# hardware device for an interface disappearing. This is
|
||||||
|
# an optional directive, and can be left out for brevity.
|
||||||
|
# This behaviour is disabled by default.
|
||||||
|
|
||||||
|
panic_on_interface_error = No
|
||||||
|
|
||||||
|
|
||||||
[logging]
|
[logging]
|
||||||
# Valid log levels are 0 through 7:
|
# Valid log levels are 0 through 7:
|
||||||
|
Loading…
Reference in New Issue
Block a user