[Python-3000] new sockets: code sample

tomer filiba tomerfiliba at gmail.com
Tue May 2 20:26:42 CEST 2006


i've written a code sample of how i think the new socket module should look.
it's only a *prototype*, but i find code is easier to explain than abstract
ideas.

i guess that if it is desired, it should be rewritten in C, or at least
work with "_socket" instead of "socket". and like GvR said, the exceptions
should be reworked.

as i said in my last (huge) post, since sockets are more complex objects
than files, and not are applicable for the "Stream protocol", they should
be kept as different entities than the new streaming IO stack.

anyway, here's the code, examples are at the bottom.


#------------------------------------------------------------------------------
import socket as _socket


class SocketError(IOError):
    pass


# could have been a metaclass, but i don't like metaclasses
def make_option_property(level, option):
    def getter(self):
        return self._sock.getsockopt(level, option)
    def setter(self, value):
        return self._sock.setsockopt(level, option, value)
    return property(getter, setter)


#------------------------------------------------------------------------------
# protocol layer mix-ins. they all inherit from object, so as not to
# create a complex class hierarchy (their are only mixins)
#------------------------------------------------------------------------------
class IpLayerMixin(object):
    """ip-specific interface"""
    ttl = make_option_property(_socket.SOL_IP, _socket.IP_TTL)
    tos = make_option_property(_socket.SOL_IP, _socket.IP_TOS)
    options = make_option_property(_socket.SOL_IP, _socket.IP_OPTIONS)
    multicast_ttl = make_option_property(_socket.SOL_IP,
_socket.IP_MULTICAST_TTL)
    multicast_loop = make_option_property(_socket.SOL_IP,
_socket.IP_MULTICAST_LOOP)
    multicast_if = make_option_property(_socket.SOL_IP, _socket.IP_MULTICAST_IF)

    has_ipv6 = _socket.has_ipv6


class TcpLayerMixin(object):
    """tcp-specific interface"""
    nodelay = make_option_property(_socket.SOL_TCP, _socket.TCP_NODELAY)


#------------------------------------------------------------------------------
# Sockets
#------------------------------------------------------------------------------
class Socket(object):
    """base socket"""
    def __init__(self, familty, type, protocol):
        self._sock = _socket.socket(familty, type, protocol)

    @classmethod
    def wrap(cls, sock):
        obj = cls.__new__(cls)
        obj._sock = sock
        return obj

    def close(self):
        self._sock.close()
    def fileno(self):
        return self._sock.fileno()

    def _get_sockname(self):
        return self._sock.getsockname()
    socketinfo = property(_get_sockname)

    def _get_blocking(self):
        return self._sock.gettimeout() == None
    def _set_blocking(self, value):
        self._sock.setblocking(bool(value))
    blocking = property(_get_blocking, _set_blocking)

    def _get_timeout(self):
        return self._sock.gettimeout()
    def _set_timeout(self, value):
        self._sock.settimeout(value)
    timeout = property(_get_timeout, _set_timeout, doc =
        "the timeout in seconds (float). for infinite timeout, set to None")

    def shutdown(self, mode):
        if mode == "r":
            self._sock.shutdown(_socket.SHUT_RD)
        elif mode == "w":
            self._sock.shutdown(_socket.SHUT_WR)
        elif mode == "rw":
            self._sock.shutdown(_socket.SHUT_RDWR)
        else:
            raise ValueError("mode can be 'r', 'w', or 'rw'")

    use_loopback = make_option_property(_socket.SOL_SOCKET,
_socket.SO_USELOOPBACK)
    allow_broadcast = make_option_property(_socket.SOL_SOCKET,
_socket.SO_BROADCAST)
    exclusive_address = make_option_property(_socket.SOL_SOCKET,
_socket.SO_EXCLUSIVEADDRUSE)
    use_keepalives = make_option_property(_socket.SOL_SOCKET,
_socket.SO_KEEPALIVE)
    reuse_address = make_option_property(_socket.SOL_SOCKET,
_socket.SO_REUSEADDR)
    dont_route = make_option_property(_socket.SOL_SOCKET, _socket.SO_DONTROUTE)


#------------------------------------------------------------------------------
# connection-oriented sockets
#------------------------------------------------------------------------------
class ServerSocket(Socket):
    """represents server sockets (bind to local address, waiting to accept)"""
    def __init__(self, familty, type, protocol, local_endpoint = None,
backlog = 1):
        Socket.__init__(self, familty, type, protocol)
        self._is_bound = False
        self._backlog = backlog
        if local_endpoint is not None:
            self.bind(local_endpoint)

    def _get_backlog(self):
        return self._backlog
    def _set_backlog(self, value):
        self._sock.listen(value)
        self._backlog = value
    backlog = property(_get_backlog, _set_backlog)

    def bind(self, local_endpoint):
        self._sock.bind(local_endpoint)
        self._sock.listen(self.backlog)
        self._is_bound = True

    def accept(self):
        if not self._is_bound:
            raise SocketError("socket is not bound")
        newsock, addrinfo = self._sock.accept()
        return newsock


class ClientSocket(Socket):
    """
    represents client sockets (connected to server).
    this is the type sockets that NetworkStream works over.
    """
    def __init__(self, familty, type, protocol, remote_endpoint):
        Socket.__init__(self, familty, type, protocol)
        self._sock.connect(remote_endpoint)

    def _get_peername(self):
        return self._sock.getpeername()
    peerinfo = property(_get_peername)

    def recv(self, count):
        try:
            data = self._sock.recv(count)
        except _socket.timeout:
            return ""
        if not data:
            return EOFError
        return data

    def send(self, data):
        return self._sock.send(data)

#------------------------------------------------------------------------------
# connection-less sockets
#------------------------------------------------------------------------------
class DatagramSocket(Socket):
    """datagram sockets"""

    def bind(self, endpoint):
        self._sock.bind(endpoint)
    def send(self, data, addr):
        return self._sock.sendto(data, addr)
    def recv(self, count):
        try:
            return self._sock.recvfrom(count)
        except _socket.timeout:
            return "", None


class RawSocket(Socket):
    """i don't know enough about raw sockets to define their interface"""
    pass


#------------------------------------------------------------------------------
# protocol-specific sockets
#------------------------------------------------------------------------------
class TcpClientSocket(ClientSocket, IpLayerMixin, TcpLayerMixin):
    def __init__(self, *args, **kwargs):
        ClientSocket.__init__(self, _socket.AF_INET, _socket.SOCK_STREAM,
        _socket.IPPROTO_TCP, *args, **kwargs)


class TcpServerSocket(ServerSocket, IpLayerMixin, TcpLayerMixin):
    def __init__(self, *args, **kwargs):
        ServerSocket.__init__(self, _socket.AF_INET, _socket.SOCK_STREAM,
        _socket.IPPROTO_TCP, *args, **kwargs)
    def accept(self):
        return TcpClientSocket.wrap( ServerSocket.accept(self) )


class UdpSocket(DatagramSocket, IpLayerMixin):
    def __init__(self, *args, **kwargs):
        DatagramSocket.__init__(self, _socket.AF_INET, _socket.SOCK_DGRAM,
        _socket.IPPROTO_UDP, *args, **kwargs)


#------------------------------------------------------------------------------
# a little client demo
#------------------------------------------------------------------------------
import Socket

s = Socket.TcpClientSocket(("localhost", 12345))
s.nodelay = True
s.send("hello")
s.recv(100)
s.close()


#------------------------------------------------------------------------------
# a little server demo
#------------------------------------------------------------------------------
import Socket

s = Socket.TcpServerSocket(("", 12345))
s2 = s.accept()

s2.recv(100)
s2.send("blah")
s2.close()

#------------------------------------------------------------------------------

only ClientSockets are applicable for streams. server sockets, datagram
sockets, etc., are all irrelevant. again, the streaming IO stack is a
separate entity than sockets, but they can be integrated as
shown below:

stream = NetworkStream(some_client_socket)

for example:

# client
s = Socket.TcpClientSocket(("localhost", 12345))
s = FramingCodec(NetworkStream(s))

# server
s = Socket.TcpServerSocket(("", 12345))
s2 = FramingCodec(NetworkStream(s.accept()))

# 's' is a server socket, but s.accept returns a ClientSocket




-tomer


More information about the Python-3000 mailing list