import socket
import time
import Queue
from net.async import Async
from net.callback import Callback
from net.error import NetError, ServerError
from net.ip import Address
from net.protocol.util import get_protocol
from net import client
[docs]class Server(object):
'''
Server connection handler. Serves as a basis for :class:`net.server.TCP`
and :class:`net.server.UDP`.
:param host: hostname or ip address
:param port: port number or serivce name
:param size: block read size
:attribute async: :class:`net.async.Async` instance
:callback onAccept(): Callback if the server accepts a new client
:callback onListen(): Callback if the server starts accepting clients
:callback onError(error): Callback if an error occurs
'''
async = Async()
def __init__(self, host, port, size=8192):
# Setup instance
self.address = Address((host, port))
self.size = size
self.family = self.address.ip.family
self.socket = None
# Setup callbacks
self.onAccept = Callback()
self.onListen = Callback()
self.onError = Callback()
# Subscribe to callbacks
self.onListen.bind(self._on_listen)
def __eq__(self, other):
'''
Compare this instance to an other instance or file descriptor. If the
``other`` argument is an integer, compare file descriptors.
'''
if isinstance(other, Server):
return id(other) == id(self)
elif type(other) == int:
if self.socket:
return self.socket.fileno() == other
else:
return False
def __enter__(self):
'''
Convenience function for constructing a ``with`` statement::
>>> with Server('127.0.0.1', 6667, protocol='line') as echo:
... print echo
... echo.serve()
'''
if not self.socket:
self.listen()
return self
def __exit__(self, exc_type, exc_value, traceback):
if isinstance(exc_type, KeyboardInterrupt):
self.async.stop()
return True
elif isinstance(exc_type, socket.error):
raise NetError(exc_value)
else:
return False
def __repr__(self):
return '<Server address=%r>' % (self.address,)
def _on_listen(self):
self.async.server(self,
self._handle_accept,
self._handle_error,
)
def _handle_accept(self, address, remote):
pass
def _handle_error(self, exc_info):
return exc_info
def _prepare(self):
return socket.socket(self.family, self.type)
[docs] def listen(self):
'''
Start listening for client connections.
'''
self.socket = self._prepare()
try:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error:
pass
try:
self.socket.bind(tuple(self.address))
self.socket.listen(1)
except socket.error, e:
raise ServerError(e)
self.onListen.call()
def serve(self):
while self.async.running:
time.sleep(0.1)
class TCPBase(Server):
type = socket.SOCK_STREAM
[docs]class TCP(TCPBase):
'''
TCP server implementation::
>>> from net.async import start
>>> from net.server import TCP
>>> start(True)
>>> with TCP('localhost', 2266, protocol='echo') as echo:
... for peer in echo:
... print 'New peer:', peer
... print >>peer, 'Welcome to the net echo server'
...
Or using a more traditional Python syntax::
>>> server = TCP('localhost', 2266, protocol='echo')
>>> server.listen()
>>> for peer in server:
... print 'New peer:', peer
... peer.write('Welcome to the net echo server\\n')
...
:param host: Server hostname or ip address
:param port: Server port or service name
:param size: Block read size
:param protocol: Server protocolcol name or :class:`net.protocol.Protocol` instance
:param args: Arguments for the server protocolcol class
:param kwargs: Keyword arguments for the server protocol class
'''
def __init__(self, host, port, protocol='line', *args, **kwargs):
TCPBase.__init__(self, host, port)
# Create protocol instance
if type(protocol) in [str, unicode]:
self.protocol = get_protocol('server', protocol)
else:
self.protocol = protocol
# Setup callbacks
self.onClientConnect = Callback()
self.onClientDisconnect = Callback()
def __iter__(self):
'''
Yields new clients.
'''
clients = Queue.Queue()
def _on_accept(peer):
clients.put(peer)
# Register callback
self.onAccept.bind(_on_accept)
while self.async.running:
try:
yield clients.get(True, 0.1)
except Queue.Empty:
pass
except KeyboardInterrupt:
self.async.stop()
# Unregister callbac
self.onAccept.remove(_on_accept)
def __repr__(self):
return '<TCP Server address=%r protocol=%r>' % (self.address, self.protocol)
def _handle_accept(self, remote, address):
peer = client.TCP(address[0], address[1], remote=remote, protocol=self.protocol)
peer.onConnect.addCallback(self._on_peer_connect, peer=peer)
peer.onDisconnect.addCallback(self._on_peer_disconnect, peer=peer)
# Tell the async core we have a connection
peer.onConnect.call()
# Let the callbacks know we have accepted a client
self.onAccept.call(peer)
return address, remote
def _on_peer_connect(self, peer=None):
self.onClientConnect.call(peer)
def _on_peer_disconnect(self, peer=None):
self.onClientDisconnect.call(peer)