PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/examples/networking/mud.py

http://stacklessexamples.googlecode.com/
Python | 192 lines | 157 code | 19 blank | 16 comment | 16 complexity | 1d09744d570a9d82d6f0c04495927998 MD5 | raw file
  1. #
  2. # A very simple MUD server based on the Stackless compatible sockets.
  3. #
  4. # Author: Richard Tew <richard.m.tew@gmail.com>
  5. #
  6. # This code was written to serve as an example of Stackless Python usage.
  7. # Feel free to email me with any questions, comments, or suggestions for
  8. # improvement.
  9. #
  10. import sys, traceback, weakref, logging
  11. import stackless
  12. # Monkeypatch in the 'stacklesssocket' module, so we get blocking sockets
  13. # which are Stackless compatible. This example code will avoid any use of
  14. # the Stackless sockets except through normal socket usage.
  15. import stacklesssocket
  16. #sys.modules["socket"] = stacklesssocket
  17. stacklesssocket.install()
  18. import socket
  19. class RemoteDisconnectionError(StandardError):
  20. pass
  21. class User:
  22. def __init__(self, connection):
  23. self.connection = connection
  24. # The tasklet will hold a reference to the user keeping the instance
  25. # alive as long as it is handling commands.
  26. stackless.tasklet(self.Run)()
  27. def Run(self):
  28. global server
  29. # Notify the server that a user is connected.
  30. server.RegisterUser(self)
  31. logging.info("Connected %d from %s", id(self), self.connection.clientAddress)
  32. try:
  33. while self.HandleCommand():
  34. pass
  35. self.OnUserDisconnection()
  36. except RemoteDisconnectionError:
  37. self.OnRemoteDisconnection()
  38. self.connection = None
  39. except:
  40. traceback.print_exc()
  41. finally:
  42. if self.connection:
  43. self.connection.Disconnect()
  44. self.connection = None
  45. def HandleCommand(self):
  46. self.connection.Write("> ")
  47. line = self.connection.ReadLine()
  48. words = [ word.strip() for word in line.strip().split(" ") ]
  49. verb = words[0]
  50. if verb == "look":
  51. userList = server.ListUsers()
  52. self.connection.WriteLine("There are %d users connected:" % len(userList))
  53. self.connection.WriteLine("Name\tHost\t\tPort")
  54. self.connection.WriteLine("-" * 40)
  55. for user in userList:
  56. host, port = user.connection.clientAddress
  57. self.connection.WriteLine("Unknown\t"+ str(host) +"\t"+ str(port))
  58. elif verb == "say":
  59. line = line[4:]
  60. secondPartyPrefix = "Someone says: "
  61. for user in server.ListUsers():
  62. if user is self:
  63. prefix = "You say: "
  64. else:
  65. prefix = secondPartyPrefix
  66. user.connection.WriteLine(prefix + "\"%s\"" % line)
  67. elif verb == "quit":
  68. return False
  69. elif verb == "help":
  70. self.connection.WriteLine("Commands:")
  71. for verb in [ "look", "say", "quit", "help" ]:
  72. self.connection.WriteLine(" "+ verb)
  73. else:
  74. self.connection.WriteLine("Unknown command. Type 'help' to see a list of available commands.")
  75. return True
  76. def OnRemoteDisconnection(self):
  77. logging.info("Disconnected %d (remote)", id(self))
  78. def OnUserDisconnection(self):
  79. logging.info("Disconnected %d (local)", id(self))
  80. class Connection:
  81. disconnected = False
  82. def __init__(self, clientSocket, clientAddress):
  83. self.clientSocket = clientSocket
  84. self.clientAddress = clientAddress
  85. self.readBuffer = ""
  86. self.userID = id(User(self))
  87. def Disconnect(self):
  88. if self.disconnected:
  89. raise RuntimeError("Unexpected call")
  90. self.disconnected = True
  91. self.clientSocket.close()
  92. def Write(self, s):
  93. self.clientSocket.send(s)
  94. def WriteLine(self, s):
  95. self.Write(s +"\r\n")
  96. def ReadLine(self):
  97. global server
  98. s = self.readBuffer
  99. while True:
  100. # If there is a CRLF in the text we have, we have a full
  101. # line to return to the caller.
  102. if s.find('\r\n') > -1:
  103. i = s.index('\r\n')
  104. # Strip the CR LF.
  105. line = s[:i]
  106. self.readBuffer = s[i+2:]
  107. while '\x08' in line:
  108. i = line.index('\x08')
  109. if i == 0:
  110. line = line[1:]
  111. else:
  112. line = line[:i-1] + line[i+1:]
  113. return line
  114. # An empty string indicates disconnection.
  115. v = self.clientSocket.recv(1000)
  116. if v == "":
  117. self.disconnected = True
  118. raise RemoteDisconnectionError
  119. s += v
  120. class Server:
  121. def __init__(self, host, port):
  122. self.host = host
  123. self.port = port
  124. self.userIndex = weakref.WeakValueDictionary()
  125. stackless.tasklet(self.Run)()
  126. def Run(self):
  127. listenSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  128. listenSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  129. listenSocket.bind((self.host, self.port))
  130. listenSocket.listen(5)
  131. logging.info("Accepting connections on %s %s", self.host, self.port)
  132. try:
  133. while 1:
  134. clientSocket, clientAddress = listenSocket.accept()
  135. Connection(clientSocket, clientAddress)
  136. stackless.schedule()
  137. except socket.error:
  138. traceback.print_exc()
  139. def RegisterUser(self, user):
  140. self.userIndex[id(user)] = user
  141. def ListUsers(self):
  142. return [ v for v in self.userIndex.itervalues() ]
  143. def Run(host, port):
  144. global server
  145. server = Server(host, port)
  146. while 1:
  147. stackless.run()
  148. if __name__ == "__main__":
  149. logging.basicConfig(level=logging.DEBUG,
  150. format='%(asctime)s %(levelname)s %(message)s')
  151. try:
  152. Run("127.0.0.1", 3000)
  153. except KeyboardInterrupt:
  154. logging.info("Server manually stopped")