Browse Source

Reworked reconnect code #24

* reconnect is no longer done automatically
* added `reconnect` method to SteamClient implementing exp backoff
pull/34/head
Rossen Georgiev 9 years ago
parent
commit
7af1c66938
  1. 21
      steam/client/__init__.py
  2. 31
      steam/core/cm.py

21
steam/client/__init__.py

@ -22,6 +22,7 @@ class SteamClient(CMClient, BuiltinBase):
See `gevent-eventmitter <https://github.com/rossengeorgiev/gevent-eventemitter>`_ See `gevent-eventmitter <https://github.com/rossengeorgiev/gevent-eventemitter>`_
for details on how to work with events. for details on how to work with events.
""" """
_reconnect_backoff_c = 0
current_jobid = 0 current_jobid = 0
credential_location = None #: location for sentry credential_location = None #: location for sentry
username = None #: username when logged on username = None #: username when logged on
@ -86,6 +87,7 @@ class SteamClient(CMClient, BuiltinBase):
result = EResult(msg.body.eresult) result = EResult(msg.body.eresult)
if result == EResult.OK: if result == EResult.OK:
self._reconnect_backoff_c = 0
self.logged_on = True self.logged_on = True
self.set_persona(EPersonaState.Online) self.set_persona(EPersonaState.Online)
self.emit("logged_on") self.emit("logged_on")
@ -134,6 +136,25 @@ class SteamClient(CMClient, BuiltinBase):
self.send(resp) self.send(resp)
def reconnect(self, maxdelay=30, retry=0):
"""Implements explonential backoff delay before attempting to connect.
It is otherwise identical to calling :meth:`steam.core.cm.CMClient.connect`.
The delay is reset upon a successful login.
:param maxdelay: maximum delay in seconds before connect (0-120s)
:type maxdelay: :class:`int`
:param retry: see :meth:`steam.core.cm.CMClient.connect`
:type retry: :class:`int`
:return: successful connection
:rtype: :class:`bool`
"""
delay_seconds = 2**self._reconnect_backoff_c - 1
if delay_seconds < maxdelay:
self._reconnect_backoff_c = min(7, self._reconnect_backoff_c + 1)
return self.connect(delay=delay_seconds, retry=retry)
def send(self, message): def send(self, message):
""" """
Send a message to CM Send a message to CM

31
steam/core/cm.py

@ -50,7 +50,6 @@ class CMClient(EventEmitter):
_recv_loop = None _recv_loop = None
_heartbeat_loop = None _heartbeat_loop = None
_reconnect_backoff_c = 0
_LOG = None _LOG = None
def __init__(self, protocol=0): def __init__(self, protocol=0):
@ -72,11 +71,13 @@ class CMClient(EventEmitter):
self._LOG.debug("Emit event: %s" % repr(event)) self._LOG.debug("Emit event: %s" % repr(event))
super(CMClient, self).emit(event, *args) super(CMClient, self).emit(event, *args)
def connect(self, retry=None): def connect(self, retry=None, delay=0):
"""Initiate connection to CM. Blocks until connected unless ``retry`` is specified. """Initiate connection to CM. Blocks until connected unless ``retry`` is specified.
:param retry: number of retries before returning. Unlimited when set to ``None`` :param retry: number of retries before returning. Unlimited when set to ``None``
:type retry: :class:`int` :type retry: :class:`int`
:param delay: delay in secnds before connection attempt
:type delay: :class:`int`
:return: successful connection :return: successful connection
:rtype: :class:`bool` :rtype: :class:`bool`
""" """
@ -88,6 +89,10 @@ class CMClient(EventEmitter):
return return
self._connecting = True self._connecting = True
if delay:
self._LOG.debug("Delayed connect: %d seconds" % delay)
gevent.sleep(delay)
self._LOG.debug("Connect initiated.") self._LOG.debug("Connect initiated.")
for i, server_addr in enumerate(self.servers): for i, server_addr in enumerate(self.servers):
@ -113,7 +118,7 @@ class CMClient(EventEmitter):
self._connecting = False self._connecting = False
return True return True
def disconnect(self, reconnect=False, nodelay=False): def disconnect(self):
"""Close connection """Close connection
.. note:: .. note::
@ -145,19 +150,6 @@ class CMClient(EventEmitter):
self._reset_attributes() self._reset_attributes()
if reconnect:
if nodelay:
delay_seconds = 0
self._reconnect_backoff_c = 0
else:
delay_seconds = 2**self._reconnect_backoff_c - 1
self._reconnect_backoff_c = min(5, self._reconnect_backoff_c + 1)
self.emit('reconnect', delay_seconds)
gevent.spawn_later(delay_seconds, self.connect)
else:
self._reconnect_backoff_c = 0
self.emit('disconnected') self.emit('disconnected')
def _reset_attributes(self): def _reset_attributes(self):
@ -286,14 +278,14 @@ class CMClient(EventEmitter):
if result is None: if result is None:
self.servers.mark_bad(self.current_server_addr) self.servers.mark_bad(self.current_server_addr)
gevent.spawn(self.disconnect, True) gevent.spawn(self.disconnect)
return return
eresult = result[0].body.eresult eresult = result[0].body.eresult
if eresult != EResult.OK: if eresult != EResult.OK:
self._LOG.error("Failed to secure channel: %s" % eresult) self._LOG.error("Failed to secure channel: %s" % eresult)
gevent.spawn(self.disconnect, True) gevent.spawn(self.disconnect)
return return
self.channel_key = key self.channel_key = key
@ -345,10 +337,9 @@ class CMClient(EventEmitter):
EResult.ServiceUnavailable EResult.ServiceUnavailable
): ):
self.servers.mark_bad(self.current_server_addr) self.servers.mark_bad(self.current_server_addr)
self.disconnect(True) self.disconnect()
elif result == EResult.OK: elif result == EResult.OK:
self._seen_logon = True self._seen_logon = True
self._reconnect_backoff_c = 0
self._LOG.debug("Logon completed") self._LOG.debug("Logon completed")

Loading…
Cancel
Save