[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