Skip to content

Commit

Permalink
keep the sockets non-blocking (the settimeout call made them blocking…
Browse files Browse the repository at this point in the history
…) and handle connection timeouts with a task like the idle timeout
  • Loading branch information
JoelBender committed May 12, 2017
1 parent 7a7b069 commit 5698bc9
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 16 deletions.
55 changes: 47 additions & 8 deletions py27/bacpypes/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def __init__(self, peer):
# ask the dispatcher for a socket
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

# set the timeout
self.socket.settimeout(self._connect_timeout)
if _debug: TCPClient._debug(" - timeout: %r", self._connect_timeout)
# make sure the connection attempt is non-blocking
self.socket.setblocking(0)
if _debug: TCPClient._debug(" - non-blocking")

# save the peer
self.peer = peer
Expand Down Expand Up @@ -138,6 +138,7 @@ def handle_accept(self):

def handle_connect(self):
if _debug: TCPClient._debug("handle_connect")
self.connected = True

def handle_connect_event(self):
if _debug: TCPClient._debug("handle_connect_event")
Expand Down Expand Up @@ -185,6 +186,9 @@ def handle_read(self):
self.handle_error(err)

def writable(self):
if not self.connected:
return True

return (len(self.request) != 0)

def handle_write(self):
Expand Down Expand Up @@ -219,14 +223,16 @@ def handle_write_event(self):
if err == 0:
if not self.connected:
if _debug: TCPClient._debug(" - connected")
self.connected = True
self.handle_connect()
else:
if _debug: TCPClient._debug(" - peer: %r", self.peer)

if (err == errno.ECONNREFUSED):
socket_error = socket.error(err, "connection refused")
elif (err == errno.ETIMEDOUT):
socket_error = socket.error(err, "timed out")
elif (err == errno.EHOSTUNREACH):
socket_error = socket.error(err, "host unreachable")
else:
socket_error = socket.error(err, "other unknown: %r" % (err,))
if _debug: TCPClient._debug(" - socket_error: %r", socket_error)
Expand Down Expand Up @@ -284,9 +290,13 @@ def __init__(self, director, peer):
self.director = None
self._connection_error = None

# pass along the connect timeout from the director
if director.connect_timeout is not None:
self._connect_timeout = director.connect_timeout
# add a timer
self._connect_timeout = director.connect_timeout
if self._connect_timeout:
self.connect_timeout_task = FunctionTask(self.connect_timeout)
self.connect_timeout_task.install_task(_time() + self._connect_timeout)
else:
self.connect_timeout_task = None

# continue with initialization
TCPClient.__init__(self, peer)
Expand All @@ -313,6 +323,23 @@ def __init__(self, director, peer):
if _debug: TCPClientActor._debug(" - had connection error")
self.director.actor_error(self, self._connection_error)

def handle_connect(self):
if _debug: TCPClientActor._debug("handle_connect")

# see if we are already connected
if self.connected:
if _debug: TCPClientActor._debug(" - already connected")
return

# if the connection timeout is scheduled, suspend it
if self.connect_timeout_task:
if _debug: TCPClientActor._debug(" - canceling connection timeout")
self.connect_timeout_task.suspend_task()
self.connect_timeout_task = None

# contine as expected
TCPClient.handle_connect(self)

def handle_error(self, error=None):
"""Trap for TCPClient errors, otherwise continue."""
if _debug: TCPClientActor._debug("handle_error %r", error)
Expand All @@ -334,16 +361,28 @@ def handle_close(self):
if self.flush_task:
self.flush_task.suspend_task()

# cancel the timer
# cancel the timers
if self.connect_timeout_task:
if _debug: TCPClientActor._debug(" - canceling connection timeout")
self.connect_timeout_task.suspend_task()
self.connect_timeout_task = None
if self.idle_timeout_task:
if _debug: TCPClientActor._debug(" - canceling idle timeout")
self.idle_timeout_task.suspend_task()
self.idle_timeout_task = None

# tell the director this is gone
self.director.del_actor(self)

# pass the function along
TCPClient.handle_close(self)

def connect_timeout(self):
if _debug: TCPClientActor._debug("connect_timeout")

# shut it down
self.handle_close()

def idle_timeout(self):
if _debug: TCPClientActor._debug("idle_timeout")

Expand Down
55 changes: 47 additions & 8 deletions py34/bacpypes/tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ def __init__(self, peer):
# ask the dispatcher for a socket
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)

# set the timeout
self.socket.settimeout(self._connect_timeout)
if _debug: TCPClient._debug(" - timeout: %r", self._connect_timeout)
# make sure the connection attempt is non-blocking
self.socket.setblocking(0)
if _debug: TCPClient._debug(" - non-blocking")

# save the peer
self.peer = peer
Expand Down Expand Up @@ -138,6 +138,7 @@ def handle_accept(self):

def handle_connect(self):
if _debug: TCPClient._debug("handle_connect")
self.connected = True

def handle_connect_event(self):
if _debug: TCPClient._debug("handle_connect_event")
Expand Down Expand Up @@ -185,6 +186,9 @@ def handle_read(self):
self.handle_error(err)

def writable(self):
if not self.connected:
return True

return (len(self.request) != 0)

def handle_write(self):
Expand Down Expand Up @@ -219,14 +223,16 @@ def handle_write_event(self):
if err == 0:
if not self.connected:
if _debug: TCPClient._debug(" - connected")
self.connected = True
self.handle_connect()
else:
if _debug: TCPClient._debug(" - peer: %r", self.peer)

if (err == errno.ECONNREFUSED):
socket_error = socket.error(err, "connection refused")
elif (err == errno.ETIMEDOUT):
socket_error = socket.error(err, "timed out")
elif (err == errno.EHOSTUNREACH):
socket_error = socket.error(err, "host unreachable")
else:
socket_error = socket.error(err, "other unknown: %r" % (err,))
if _debug: TCPClient._debug(" - socket_error: %r", socket_error)
Expand Down Expand Up @@ -284,9 +290,13 @@ def __init__(self, director, peer):
self.director = None
self._connection_error = None

# pass along the connect timeout from the director
if director.connect_timeout is not None:
self._connect_timeout = director.connect_timeout
# add a timer
self._connect_timeout = director.connect_timeout
if self._connect_timeout:
self.connect_timeout_task = FunctionTask(self.connect_timeout)
self.connect_timeout_task.install_task(_time() + self._connect_timeout)
else:
self.connect_timeout_task = None

# continue with initialization
TCPClient.__init__(self, peer)
Expand All @@ -313,6 +323,23 @@ def __init__(self, director, peer):
if _debug: TCPClientActor._debug(" - had connection error")
self.director.actor_error(self, self._connection_error)

def handle_connect(self):
if _debug: TCPClientActor._debug("handle_connect")

# see if we are already connected
if self.connected:
if _debug: TCPClientActor._debug(" - already connected")
return

# if the connection timeout is scheduled, suspend it
if self.connect_timeout_task:
if _debug: TCPClientActor._debug(" - canceling connection timeout")
self.connect_timeout_task.suspend_task()
self.connect_timeout_task = None

# contine as expected
TCPClient.handle_connect(self)

def handle_error(self, error=None):
"""Trap for TCPClient errors, otherwise continue."""
if _debug: TCPClientActor._debug("handle_error %r", error)
Expand All @@ -334,16 +361,28 @@ def handle_close(self):
if self.flush_task:
self.flush_task.suspend_task()

# cancel the timer
# cancel the timers
if self.connect_timeout_task:
if _debug: TCPClientActor._debug(" - canceling connection timeout")
self.connect_timeout_task.suspend_task()
self.connect_timeout_task = None
if self.idle_timeout_task:
if _debug: TCPClientActor._debug(" - canceling idle timeout")
self.idle_timeout_task.suspend_task()
self.idle_timeout_task = None

# tell the director this is gone
self.director.del_actor(self)

# pass the function along
TCPClient.handle_close(self)

def connect_timeout(self):
if _debug: TCPClientActor._debug("connect_timeout")

# shut it down
self.handle_close()

def idle_timeout(self):
if _debug: TCPClientActor._debug("idle_timeout")

Expand Down

0 comments on commit 5698bc9

Please sign in to comment.