move address-family from tcp/socket interface to endpoint interface. The address family of a socket is strictly a property of the address passed to it
"""
TCP service/client implementation.
"""
from qmsk.net.transport import service, client, stream, socket
# default backlog for listen()
# XXX: number pulled out of a hat
LISTEN_BACKLOG = 5
class Connection (socket.Stream, stream.Stream) :
"""
Stream interface for a TCP connection
"""
def __init__ (self, sock) :
"""
Initialize with the given already-existing, connected, socket.
"""
self._init_sock(sock)
def shutdown (self, how) :
"""
Selectively shut-down parts of all of the full-duplex TCP connection.
how - one of socket.SHUT_* to shutdown read, write or both.
"""
self.sock.shutdown(how)
class Service (socket.Service, service.Service) :
"""
An implementation of Service for TCP sockets.
"""
_SOCKTYPE = socket.SOCK_STREAM
def __init__ (self, endpoint, listen_backlog=LISTEN_BACKLOG, family=None) :
"""
Construct a service, bound to the given local endpoint and listening for incoming connections using the
given backlog.
endpoint - local Endpoint to bind() to. Usually, it is enough to just specify the port.
listen_backlog - backlog length argument to use for socket.listen()
family - (optional) address family to use if no endpoint is given
Note that as a special case, it is possible to construct a service without an Endpoint (i.e. None).
In this case, there will be no socket.bind() call, instead, a socket is created with the given address
family (which *MUST* be given), and .listen() causes the OS to pick a local address to use.
This will raise an error if the bind() or listen() operations fail.
"""
# construct a suitable socket bound to the given endpoint
self._init_endpoint(endpoint, family=family)
# make us listen
self._listen(listen_backlog)
# ok, great
def accept (self, cls=Connection) :
"""
Perform an accept() operation on our socket, returning a tcp.Connection.
"""
# XXX: trap and raise a ServiceAcceptError?
# XXX: what to do with addr?
sock, addr = self.sock.accept()
# construct the new Stream
return cls(sock)
def close (self) :
"""
Close the underlying socket object, invalidating this Service for future use.
This will raise if the underlying socket.close() operation does.
"""
self.sock.close()
class Client (socket.Client, client.Client) :
"""
An implementation of Client for TCP sockets.
"""
_SOCKTYPE = socket.SOCK_STREAM
def __init__ (self, connect_endpoint, bind_endpoint=None) :
"""
Construct a client, connecting to the given remote endpoint.
connect_endpoint - remote Endpoint to connect() to.
bind_endpoint - (optional) local Endpoint to bind() to before connecting.
"""
# store
self.connect_endpoint = connect_endpoint
self.bind_endpoint = bind_endpoint
def connect (self, cls=Connection) :
"""
Perform the connect() operation, returning a tcp.Connection.
"""
if self.bind_endpoint :
# construct a suitable local socket, bound to a specific endpoint
sock = self._bind_endpoint(self.bind_endpoint)
# connect it to the remote endpoint
self._connect_sock_endpoint(sock, self.connect_endpoint)
else :
# let _init_connect_endpoint pick a socket to use
sock = self._connect_endpoint(self.connect_endpoint)
# construct
return cls(sock)