network.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #!/usr/bin/env python3
  2. class NetAddr:
  3. """A class representing a network address"""
  4. nextaddr = 1
  5. def __init__(self):
  6. """Generate a fresh network address"""
  7. self.addr = NetAddr.nextaddr
  8. NetAddr.nextaddr += 1
  9. def __eq__(self, other):
  10. return (isinstance(other, self.__class__)
  11. and self.__dict__ == other.__dict__)
  12. def __hash__(self):
  13. return hash(self.addr)
  14. def __str__(self):
  15. return self.addr.__str__()
  16. class Network:
  17. """A class representing a simulated network. Servers can bind()
  18. to the network, yielding a NetAddr (network address), and clients
  19. can connect() to a NetAddr yielding a Connection."""
  20. def __init__(self):
  21. self.servers = dict()
  22. self.epoch = 1
  23. self.epochcallbacks = []
  24. def printservers(self):
  25. """Print the list of NetAddrs bound to something."""
  26. print("Servers:")
  27. for a in self.servers.keys():
  28. print(a)
  29. def getepoch(self):
  30. """Return the current epoch."""
  31. return self.epoch
  32. def nextepoch(self):
  33. """Increment the current epoch, and return it."""
  34. self.epoch += 1
  35. for c in self.epochcallbacks:
  36. c.newepoch(self.epoch)
  37. return self.epoch
  38. def wantepochticks(self, callback, want):
  39. """Register or deregister an object from receiving epoch change
  40. callbacks. If want is True, the callback object's newepoch()
  41. method will be called at each epoch change, with an argument of
  42. the new epoch. If want if False, the callback object will be
  43. deregistered."""
  44. if want:
  45. self.epochcallbacks.append(callback)
  46. else:
  47. self.epochcallbacks.remove(callback)
  48. def bind(self, server):
  49. """Bind a server to a newly generated NetAddr, returning the
  50. NetAddr. The server's bound() callback will also be invoked."""
  51. addr = NetAddr()
  52. self.servers[addr] = server
  53. server.bound(addr, lambda: self.servers.pop(addr))
  54. return addr
  55. def connect(self, client, srvaddr):
  56. """Connect the given client to the server bound to addr. Throw
  57. an exception if there is no server bound to that address."""
  58. server = self.servers[srvaddr]
  59. return server.connected(client)
  60. # The singleton instance of Network
  61. thenetwork = Network()
  62. class NetMsg:
  63. """The parent class of network messages. Subclass this class to
  64. implement specific kinds of network messages."""
  65. class StringNetMsg(NetMsg):
  66. """Send an arbitratry string as a NetMsg."""
  67. def __init__(self, str):
  68. self.data = str
  69. def __str__(self):
  70. return self.data.__str__()
  71. class Connection:
  72. def __init__(self, peer = None):
  73. """Create a Connection object with the given peer."""
  74. self.peer = peer
  75. def closed(self):
  76. print("connection closed with", self.peer)
  77. self.peer = None
  78. def close(self):
  79. print("closing connection with", self.peer)
  80. self.peer.closed()
  81. self.peer = None
  82. class ClientConnection(Connection):
  83. """The parent class of client-side network connections. Subclass
  84. this class to do anything more elaborate than just passing arbitrary
  85. NetMsgs, which then get ignored."""
  86. def __init__(self, peer):
  87. """Create a ClientConnection object with the given peer. The
  88. peer must have a received(client, msg) method."""
  89. self.peer = peer
  90. def sendmsg(self, netmsg):
  91. assert(isinstance(netmsg, NetMsg))
  92. self.peer.received(self, netmsg)
  93. def received(self, netmsg):
  94. print("received", netmsg, "from server")
  95. class ServerConnection(Connection):
  96. """The parent class of server-side network connections."""
  97. def __init__(self):
  98. self.peer = None
  99. def sendmsg(self, netmsg):
  100. assert(isinstance(netmsg, NetMsg))
  101. self.peer.received(netmsg)
  102. def received(self, client, netmsg):
  103. print("received", netmsg, "from client", client)
  104. class Server:
  105. """The parent class of network servers. Subclass this class to
  106. implement servers of different kinds. You will probably only need
  107. to override the implementation of connected()."""
  108. def __init__(self, name):
  109. self.name = name
  110. def __str__(self):
  111. return self.name.__str__()
  112. def bound(self, netaddr, closer):
  113. """Callback invoked when the server is successfully bound to a
  114. NetAddr. The parameters are the netaddr to which the server is
  115. bound and closer (as in a thing that causes something to close)
  116. is a callback function that should be used when the server
  117. wishes to stop listening for new connections."""
  118. self.closer = closer
  119. def close(self):
  120. """Stop listening for new connections."""
  121. self.closer()
  122. def connected(self, client):
  123. """Callback invoked when a client connects to this server.
  124. This callback must create the Connection object that will be
  125. returned to the client."""
  126. print("server", self, "conected to client", client)
  127. serverconnection = ServerConnection()
  128. clientconnection = ClientConnection(serverconnection)
  129. serverconnection.peer = clientconnection
  130. return clientconnection
  131. if __name__ == '__main__':
  132. n1 = NetAddr()
  133. n2 = NetAddr()
  134. assert(n1 == n1)
  135. assert(not (n1 == n2))
  136. assert(n1 != n2)
  137. print(n1, n2)
  138. srv = Server("hello world server")
  139. thenetwork.printservers()
  140. a = thenetwork.bind(srv)
  141. thenetwork.printservers()
  142. print("in main", a)
  143. conn = thenetwork.connect("hello world client", a)
  144. conn.sendmsg(StringNetMsg("hi"))
  145. conn.close()
  146. srv.close()
  147. thenetwork.printservers()