|
@@ -0,0 +1,187 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+
|
|
|
+class NetAddr:
|
|
|
+ """A class representing a network address"""
|
|
|
+ nextaddr = 1
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ """Generate a fresh network address"""
|
|
|
+ self.addr = NetAddr.nextaddr
|
|
|
+ NetAddr.nextaddr += 1
|
|
|
+
|
|
|
+ def __eq__(self, other):
|
|
|
+ return (isinstance(other, self.__class__)
|
|
|
+ and self.__dict__ == other.__dict__)
|
|
|
+
|
|
|
+ def __hash__(self):
|
|
|
+ return hash(self.addr)
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return self.addr.__str__()
|
|
|
+
|
|
|
+class Network:
|
|
|
+ """A class representing a simulated network. Servers can bind()
|
|
|
+ to the network, yielding a NetAddr (network address), and clients
|
|
|
+ can connect() to a NetAddr yielding a Connection."""
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.servers = dict()
|
|
|
+ self.epoch = 1
|
|
|
+ self.epochcallbacks = []
|
|
|
+
|
|
|
+ def printservers(self):
|
|
|
+ """Print the list of NetAddrs bound to something."""
|
|
|
+ print("Servers:")
|
|
|
+ for a in self.servers.keys():
|
|
|
+ print(a)
|
|
|
+
|
|
|
+ def getepoch(self):
|
|
|
+ """Return the current epoch."""
|
|
|
+ return self.epoch
|
|
|
+
|
|
|
+ def nextepoch(self):
|
|
|
+ """Increment the current epoch, and return it."""
|
|
|
+ self.epoch += 1
|
|
|
+ for c in self.epochcallbacks:
|
|
|
+ c.newepoch(self.epoch)
|
|
|
+ return self.epoch
|
|
|
+
|
|
|
+ def wantepochticks(self, callback, want):
|
|
|
+ """Register or deregister an object from receiving epoch change
|
|
|
+ callbacks. If want is True, the callback object's newepoch()
|
|
|
+ method will be called at each epoch change, with an argument of
|
|
|
+ the new epoch. If want if False, the callback object will be
|
|
|
+ deregistered."""
|
|
|
+ if want:
|
|
|
+ self.epochcallbacks.append(callback)
|
|
|
+ else:
|
|
|
+ self.epochcallbacks.remove(callback)
|
|
|
+
|
|
|
+ def bind(self, server):
|
|
|
+ """Bind a server to a newly generated NetAddr, returning the
|
|
|
+ NetAddr. The server's bound() callback will also be invoked."""
|
|
|
+ addr = NetAddr()
|
|
|
+ self.servers[addr] = server
|
|
|
+ server.bound(addr, lambda: self.servers.pop(addr))
|
|
|
+ return addr
|
|
|
+
|
|
|
+ def connect(self, client, srvaddr):
|
|
|
+ """Connect the given client to the server bound to addr. Throw
|
|
|
+ an exception if there is no server bound to that address."""
|
|
|
+ server = self.servers[srvaddr]
|
|
|
+ return server.connected(client)
|
|
|
+
|
|
|
+# The singleton instance of Network
|
|
|
+thenetwork = Network()
|
|
|
+
|
|
|
+class NetMsg:
|
|
|
+ """The parent class of network messages. Subclass this class to
|
|
|
+ implement specific kinds of network messages."""
|
|
|
+
|
|
|
+class StringNetMsg(NetMsg):
|
|
|
+ """Send an arbitratry string as a NetMsg."""
|
|
|
+ def __init__(self, str):
|
|
|
+ self.data = str
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return self.data.__str__()
|
|
|
+
|
|
|
+class Connection:
|
|
|
+ def __init__(self, peer = None):
|
|
|
+ """Create a Connection object with the given peer."""
|
|
|
+ self.peer = peer
|
|
|
+
|
|
|
+ def closed(self):
|
|
|
+ print("connection closed with", self.peer)
|
|
|
+ self.peer = None
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ print("closing connection with", self.peer)
|
|
|
+ self.peer.closed()
|
|
|
+ self.peer = None
|
|
|
+
|
|
|
+class ClientConnection(Connection):
|
|
|
+ """The parent class of client-side network connections. Subclass
|
|
|
+ this class to do anything more elaborate than just passing arbitrary
|
|
|
+ NetMsgs, which then get ignored."""
|
|
|
+
|
|
|
+ def __init__(self, peer):
|
|
|
+ """Create a ClientConnection object with the given peer. The
|
|
|
+ peer must have a received(client, msg) method."""
|
|
|
+ self.peer = peer
|
|
|
+
|
|
|
+ def sendmsg(self, netmsg):
|
|
|
+ assert(isinstance(netmsg, NetMsg))
|
|
|
+ self.peer.received(self, netmsg)
|
|
|
+
|
|
|
+ def received(self, netmsg):
|
|
|
+ print("received", netmsg, "from server")
|
|
|
+
|
|
|
+
|
|
|
+class ServerConnection(Connection):
|
|
|
+ """The parent class of server-side network connections."""
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.peer = None
|
|
|
+
|
|
|
+ def sendmsg(self, netmsg):
|
|
|
+ assert(isinstance(netmsg, NetMsg))
|
|
|
+ self.peer.received(netmsg)
|
|
|
+
|
|
|
+ def received(self, client, netmsg):
|
|
|
+ print("received", netmsg, "from client", client)
|
|
|
+
|
|
|
+class Server:
|
|
|
+ """The parent class of network servers. Subclass this class to
|
|
|
+ implement servers of different kinds. You will probably only need
|
|
|
+ to override the implementation of connected()."""
|
|
|
+
|
|
|
+ def __init__(self, name):
|
|
|
+ self.name = name
|
|
|
+
|
|
|
+ def __str__(self):
|
|
|
+ return self.name.__str__()
|
|
|
+
|
|
|
+ def bound(self, netaddr, closer):
|
|
|
+ """Callback invoked when the server is successfully bound to a
|
|
|
+ NetAddr. The parameters are the netaddr to which the server is
|
|
|
+ bound and closer (as in a thing that causes something to close)
|
|
|
+ is a callback function that should be used when the server
|
|
|
+ wishes to stop listening for new connections."""
|
|
|
+ self.closer = closer
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ """Stop listening for new connections."""
|
|
|
+ self.closer()
|
|
|
+
|
|
|
+ def connected(self, client):
|
|
|
+ """Callback invoked when a client connects to this server.
|
|
|
+ This callback must create the Connection object that will be
|
|
|
+ returned to the client."""
|
|
|
+ print("server", self, "conected to client", client)
|
|
|
+ serverconnection = ServerConnection()
|
|
|
+ clientconnection = ClientConnection(serverconnection)
|
|
|
+ serverconnection.peer = clientconnection
|
|
|
+ return clientconnection
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ n1 = NetAddr()
|
|
|
+ n2 = NetAddr()
|
|
|
+ assert(n1 == n1)
|
|
|
+ assert(not (n1 == n2))
|
|
|
+ assert(n1 != n2)
|
|
|
+ print(n1, n2)
|
|
|
+
|
|
|
+ srv = Server("hello world server")
|
|
|
+
|
|
|
+ thenetwork.printservers()
|
|
|
+ a = thenetwork.bind(srv)
|
|
|
+ thenetwork.printservers()
|
|
|
+ print("in main", a)
|
|
|
+
|
|
|
+ conn = thenetwork.connect("hello world client", a)
|
|
|
+ conn.sendmsg(StringNetMsg("hi"))
|
|
|
+ conn.close()
|
|
|
+
|
|
|
+ srv.close()
|
|
|
+ thenetwork.printservers()
|