Implement and turn on the osnet demux

This change introduces a demux that uses the kernel's network stack for UDP
datagram-to-socket assignment based on packet source address (as opposed to the
forwarding strategy of the routing demux). The osnet demux is used by default
on non-Windows platforms. When possible, use of the osnet demux is preferred
over the routing demux, since it can be expected to perform better.

The unit test suite has been extended to run all tests first with the demux
selected by default for the current platform, and then with the routing demux,
if the latter differs from the former. Tests were already being run twice, first
with IPv4 and then with IPv6, and thus we now run each test four times on
Linux, twice on Windows.

All unit tests pass with both demux types.
This commit is contained in:
Ray Brown committed 2012-11-27 21:33:09 -08:00
1 parent ca32b91a9c
commit 77a50c7382
5 files changed
+134 -44

No files matched your search

+23 -3
View File
@@ -20,8 +20,28 @@ import sys
if sys.platform.startswith('win') or sys.platform.startswith('cygwin'):
from router import UDPDemux
_routing = True
else:
#from osnet import UDPDemux
from router import UDPDemux
from osnet import UDPDemux
_routing = False
_default_demux = None
__all__ = ["UDPDemux"]
def force_routing_demux():
global _routing
if _routing:
return False # no change - already loaded
global UDPDemux, _default_demux
import router
_default_demux = UDPDemux
UDPDemux = router.UDPDemux
_routing = True
return True # new router loaded and switched
def reset_default_demux():
global UDPDemux, _routing, _default_demux
if _default_demux:
UDPDemux = _default_demux
_default_demux = None
_routing = not _routing
__all__ = ["UDPDemux", "force_routing_demux", "reset_default_demux"]
+52 -10
View File
@@ -17,9 +17,16 @@ Classes:
Exceptions:
InvalidSocketError -- exception raised for improper socket objects
KeyError -- raised for unknown peer addresses
"""
import socket
from logging import getLogger
from ..err import InvalidSocketError
_logger = getLogger(__name__)
class UDPDemux(object):
"""OS network stack configuring demux
@@ -31,10 +38,34 @@ class UDPDemux(object):
Methods:
get_connection -- create a new connection or retrieve an existing one
remove_connection -- remove an existing connection
service -- this method does nothing for this type of demux
"""
def __init__(self, datagram_socket):
"""Constructor
Arguments:
datagram_socket -- the root socket; this must be a bound, unconnected
datagram socket
"""
if datagram_socket.type != socket.SOCK_DGRAM:
raise InvalidSocketError("datagram_socket is not of " +
"type SOCK_DGRAM")
try:
datagram_socket.getsockname()
except:
raise InvalidSocketError("datagram_socket is unbound")
try:
datagram_socket.getpeername()
except:
pass
else:
raise InvalidSocketError("datagram_socket is connected")
datagram_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._datagram_socket = datagram_socket
def get_connection(self, address):
"""Create or retrieve a muxed connection
@@ -47,16 +78,27 @@ class UDPDemux(object):
in case address was None
"""
def remove_connection(self, address):
"""Remove a muxed connection
if not address:
return self._datagram_socket
Arguments:
address -- an address for which a muxed connection was previously
retrieved through get_connection, which has not yet
been removed
# Create a new datagram socket bound to the same interface and port as
# the root socket, but connected to the given peer
conn = socket.socket(self._datagram_socket.family,
self._datagram_socket.type,
self._datagram_socket.proto)
conn.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
conn.bind(self._datagram_socket.getsockname())
conn.connect(address)
_logger.debug("Created new connection for address: %s", address)
return conn
Return:
the socket object whose connection has been removed
@staticmethod
def service():
"""Service the root socket
This type of demux performs no servicing work on the root socket,
and instead advises the caller to proceed to listening on the root
socket.
"""
return self.connections.pop(address)
return True