当前位置:   article > 正文

如何用Python搭建聊天室_python聊天室

python聊天室

项目实战(服务器端)

1.服务器类

首先需要一个聊天服务器,这里继承asyncore的dispatcher类来实现,代码如下

  1. class ChatServer(dispatcher):
  2. """
  3. 聊天服务器
  4. """
  5. def __init__(self, port):
  6. dispatcher.__init__(self)
  7. self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
  8. self.set_reuse_addr()
  9. self.bind(('', port))
  10. self.listen(5)
  11. self.users = {}
  12. self.main_room = ChatRoom(self)
  13. def handle_accept(self):
  14. conn, addr = self.accept()
  15. ChatSession(self, conn)

2.会话类

有了服务器类还需要能维护每个用户的连接会话,这里继承asynchat的async_chat类来实现,代码如下:

  1. class ChatSession(async_chat):
  2. """
  3. 负责和单用户通信
  4. """
  5. def __init__(self, server, sock):
  6. async_chat.__init__(self, sock)
  7. self.server = server
  8. self.set_terminator('\n')
  9. self.data = []
  10. self.name = None
  11. self.enter(LoginRoom(server))
  12. def enter(self, room):
  13. '从当前房间移除自身,然后添加到指定房间'
  14. try:
  15. cur = self.room
  16. except AttributeError:
  17. pass
  18. else:
  19. cur.remove(self)
  20. self.room = room
  21. room.add(self)
  22. def collect_incoming_data(self, data):
  23. '接受客户端的数据'
  24. self.data.append(data)
  25. def found_terminator(self):
  26. '当客户端的一条数据结束时的处理'
  27. line = ''.join(self.data)
  28. self.data = []
  29. try:
  30. self.room.handle(self, line)
  31. except EndSession:
  32. self.handle_close()
  33. def handle_close(self):
  34. async_chat.handle_close(self)
  35. self.enter(LogoutRoom(self.server))

3.命令解释器

现在就需要一个命令解释器能够解释用户的命令,例如登录、查询在线用户和发消息等,代码如下:

  1. class CommandHandler:
  2. """
  3. 命令处理类
  4. """
  5. def unknown(self, session, cmd):
  6. '响应未知命令'
  7. session.push('Unknown command: %s\n' % cmd)
  8. def handle(self, session, line):
  9. '命令处理'
  10. if not line.strip():
  11. return
  12. parts = line.split(' ', 1)
  13. cmd = parts[0]
  14. try:
  15. line = parts[1].strip()
  16. except IndexError:
  17. line = ''
  18. meth = getattr(self, 'do_' + cmd, None)
  19. try:
  20. meth(session, line)
  21. except TypeError:
  22. self.unknown(session, cmd)

4.房间

接下来就需要实现聊天室的房间了,这里我们定义了三种房间,分别是用户刚登录时的房间、聊天的房间和退出登录的房间,这三种房间都有一个公共的父类,代码如下:

  1. session.push('Login Success')
  2. self.broadcast(session.name + ' has entered the room.\n')
  3. self.server.users[session.name] = session
  4. Room.add(self, session)
  5. def remove(self, session):
  6. '广播用户离开'
  7. Room.remove(self, session)
  8. self.broadcast(session.name + ' has left the room.\n')
  9. def do_say(self, session, line):
  10. '客户端发送消息'
  11. self.broadcast(session.name + ': ' + line + '\n')
  12. def do_look(self, session, line):
  13. '查看在线用户'
  14. session.push('Online Users:\n')
  15. for other in self.sessions:
  16. session.push(other.name + '\n')
  17. class LogoutRoom(Room):
  18. """
  19. 用户退出时的房间
  20. """
  21. def add(self, session):
  22. '从服务器中移除'
  23. try:
  24. del self.server.users[session.name]
  25. except KeyError:
  26. pass

5.服务器端完整代码

  1. #!/usr/bin/python
  2. # encoding: utf-8
  3. from asyncore import dispatcher
  4. from asynchat import async_chat
  5. import socket, asyncore
  6. PORT = 6666 #端口
  7. class EndSession(Exception):
  8. """
  9. 自定义会话结束时的异常
  10. """
  11. pass
  12. class CommandHandler:
  13. """
  14. 命令处理类
  15. """
  16. def unknown(self, session, cmd):
  17. '响应未知命令'
  18. session.push('Unknown command: %s\n' % cmd)
  19. def handle(self, session, line):
  20. '命令处理'
  21. if not line.strip():
  22. return
  23. parts = line.split(' ', 1)
  24. cmd = parts[0]
  25. try:
  26. line = parts[1].strip()
  27. except IndexError:
  28. line = ''
  29. meth = getattr(self, 'do_' + cmd, None)
  30. try:
  31. meth(session, line)
  32. except TypeError:
  33. self.unknown(session, cmd)
  34. class Room(CommandHandler):
  35. """
  36. 包含多个用户的环境,负责基本的命令处理和广播
  37. """
  38. def __init__(self, server):
  39. self.server = server
  40. self.sessions = []
  41. def add(self, session):
  42. '一个用户进入房间'
  43. self.sessions.append(session)
  44. def remove(self, session):
  45. '一个用户离开房间'
  46. self.sessions.remove(session)
  47. def broadcast(self, line):
  48. '向所有的用户发送指定消息'
  49. for session in self.sessions:
  50. session.push(line)
  51. def do_logout(self, session, line):
  52. '退出房间'
  53. raise EndSession
  54. class LoginRoom(Room):
  55. """
  56. 刚登录的用户的房间
  57. """
  58. def add(self, session):
  59. '用户连接成功的回应'
  60. Room.add(self, session)
  61. session.push('Connect Success')
  62. def do_login(self, session, line):
  63. '登录命令处理'
  64. name = line.strip()
  65. if not name:
  66. session.push('UserName Empty')
  67. elif name in self.server.users:
  68. session.push('UserName Exist')
  69. else:
  70. session.name = name
  71. session.enter(self.server.main_room)
  72. class ChatRoom(Room):
  73. """
  74. 聊天用的房间
  75. """
  76. def add(self, session):
  77. '广播新用户进入'
  78. session.push('Login Success')
  79. self.broadcast(session.name + ' has entered the room.\n')
  80. self.server.users[session.name] = session
  81. Room.add(self, session)
  82. def remove(self, session):
  83. '广播用户离开'
  84. Room.remove(self, session)
  85. self.broadcast(session.name + ' has left the room.\n')
  86. def do_say(self, session, line):
  87. '客户端发送消息'
  88. self.broadcast(session.name + ': ' + line + '\n')
  89. def do_look(self, session, line):
  90. '查看在线用户'
  91. session.push('Online Users:\n')
  92. for other in self.sessions:
  93. session.push(other.name + '\n')
  94. class LogoutRoom(Room):
  95. """
  96. 用户退出时的房间
  97. """
  98. def add(self, session):
  99. '从服务器中移除'
  100. try:
  101. del self.server.users[session.name]
  102. except KeyError:
  103. pass
  104. class ChatSession(async_chat):
  105. """
  106. 负责和单用户通信
  107. """
  108. def __init__(self, server, sock):
  109. async_chat.__init__(self, sock)
  110. self.server = server
  111. self.set_terminator('\n')
  112. self.data = []
  113. self.name = None
  114. self.enter(LoginRoom(server))
  115. def enter(self, room):
  116. '从当前房间移除自身,然后添加到指定房间'
  117. try:
  118. cur = self.room
  119. except AttributeError:
  120. pass
  121. else:
  122. cur.remove(self)
  123. self.room = room
  124. room.add(self)
  125. def collect_incoming_data(self, data):
  126. '接受客户端的数据'
  127. self.data.append(data)
  128. def found_terminator(self):
  129. '当客户端的一条数据结束时的处理'
  130. line = ''.join(self.data)
  131. self.data = []
  132. try:
  133. self.room.handle(self, line)
  134. except EndSession:
  135. self.handle_close()
  136. def handle_close(self):
  137. async_chat.handle_close(self)
  138. self.enter(LogoutRoom(self.server))
  139. class ChatServer(dispatcher):
  140. """
  141. 聊天服务器
  142. """
  143. def __init__(self, port):
  144. dispatcher.__init__(self)
  145. self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
  146. self.set_reuse_addr()
  147. self.bind(('', port))
  148. self.listen(5)
  149. self.users = {}
  150. self.main_room = ChatRoom(self)
  151. def handle_accept(self):
  152. conn, addr = self.accept()
  153. ChatSession(self, conn)
  154. if __name__ == '__main__':
  155. s = ChatServer(PORT)
  156. try:
  157. asyncore.loop()
  158. except KeyboardInterrupt:
  159. print

三、项目实战(客户端

完成了服务器端后,就需要实现客户端了,这里客户端连接服务器使用了telnetlib模块。

1.登录窗口

这里的图形界面包选择了wxPython,前面有安装说明,登录窗口通过继承wx.Frame类来实现,代码如下:

  1. class LoginFrame(wx.Frame):
  2. """
  3. 登录窗口
  4. """
  5. def __init__(self, parent, id, title, size):
  6. '初始化,添加控件并绑定事件'
  7. wx.Frame.__init__(self, parent, id, title)
  8. self.SetSize(size)
  9. self.Center()
  10. self.serverAddressLabel = wx.StaticText(self, label = "Server Address", pos = (10, 50), size = (120, 25))
  11. self.userNameLabel = wx.StaticText(self, label = "UserName", pos = (40, 100), size = (120, 25))
  12. self.serverAddress = wx.TextCtrl(self, pos = (120, 47), size = (150, 25))
  13. self.userName = wx.TextCtrl(self, pos = (120, 97), size = (150, 25))
  14. self.loginButton = wx.Button(self, label = 'Login', pos = (80, 145), size = (130, 30))
  15. self.loginButton.Bind(wx.EVT_BUTTON, self.login)
  16. self.Show()
  17. def login(self, event):
  18. '登录处理'
  19. try:
  20. serverAddress = self.serverAddress.GetLineText(0).split(':')
  21. con.open(serverAddress[0], port = int(serverAddress[1]), timeout = 10)
  22. response = con.read_some()
  23. if response != 'Connect Success':
  24. self.showDialog('Error', 'Connect Fail!', (95, 20))
  25. return
  26. con.write('login ' + str(self.userName.GetLineText(0)) + '\n')
  27. response = con.read_some()
  28. if response == 'UserName Empty':
  29. self.showDialog('Error', 'UserName Empty!', (135, 20))
  30. elif response == 'UserName Exist':
  31. self.showDialog('Error', 'UserName Exist!', (135, 20))
  32. else:
  33. self.Close()
  34. ChatFrame(None, -2, title = 'ShiYanLou Chat Client', size = (500, 350))
  35. except Exception:
  36. self.showDialog('Error', 'Connect Fail!', (95, 20))
  37. def showDialog(self, title, content, size):
  38. '显示错误信息对话框'
  39. dialog = wx.Dialog(self, title = title, size = size)
  40. dialog.Center()
  41. wx.StaticText(dialog, label = content)
  42. dialog.ShowModal()

2.聊天窗口

聊天窗口中最主要的就是向服务器发消息并接受服务器的消息,这里通过子线程来接受,代码如下:

  1. class ChatFrame(wx.Frame):
  2. """
  3. 聊天窗口
  4. """
  5. def __init__(self, parent, id, title, size):
  6. '初始化,添加控件并绑定事件'
  7. wx.Frame.__init__(self, parent, id, title)
  8. self.SetSize(size)
  9. self.Center()
  10. self.chatFrame = wx.TextCtrl(self, pos = (5, 5), size = (490, 310), style = wx.TE_MULTILINE | wx.TE_READONLY)
  11. self.message = wx.TextCtrl(self, pos = (5, 320), size = (300, 25))
  12. self.sendButton = wx.Button(self, label = "Send", pos = (310, 320), size = (58, 25))
  13. self.usersButton = wx.Button(self, label = "Users", pos = (373, 320), size = (58, 25))
  14. self.closeButton = wx.Button(self, label = "Close", pos = (436, 320), size = (58, 25))
  15. self.sendButton.Bind(wx.EVT_BUTTON, self.send)
  16. self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
  17. self.closeButton.Bind(wx.EVT_BUTTON, self.close)
  18. thread.start_new_thread(self.receive, ())
  19. self.Show()
  20. def send(self, event):
  21. '发送消息'
  22. message = str(self.message.GetLineText(0)).strip()
  23. if message != '':
  24. con.write('say ' + message + '\n')
  25. self.message.Clear()
  26. def lookUsers(self, event):
  27. '查看当前在线用户'
  28. con.write('look\n')
  29. def close(self, event):
  30. '关闭窗口'
  31. con.write('logout\n')
  32. con.close()
  33. self.Close()
  34. def receive(self):
  35. '接受服务器的消息'
  36. while True:
  37. sleep(0.6)
  38. result = con.read_very_eager()
  39. if result != '':
  40. self.chatFrame.AppendText(result)

3.客户端完整代码

  1. #!/usr/bin/python
  2. # encoding: utf-8
  3. import wx
  4. import telnetlib
  5. from time import sleep
  6. import thread
  7. class LoginFrame(wx.Frame):
  8. """
  9. 登录窗口
  10. """
  11. def __init__(self, parent, id, title, size):
  12. '初始化,添加控件并绑定事件'
  13. wx.Frame.__init__(self, parent, id, title)
  14. self.SetSize(size)
  15. self.Center()
  16. self.serverAddressLabel = wx.StaticText(self, label = "Server Address", pos = (10, 50), size = (120, 25))
  17. self.userNameLabel = wx.StaticText(self, label = "UserName", pos = (40, 100), size = (120, 25))
  18. self.serverAddress = wx.TextCtrl(self, pos = (120, 47), size = (150, 25))
  19. self.userName = wx.TextCtrl(self, pos = (120, 97), size = (150, 25))
  20. self.loginButton = wx.Button(self, label = 'Login', pos = (80, 145), size = (130, 30))
  21. self.loginButton.Bind(wx.EVT_BUTTON, self.login)
  22. self.Show()
  23. def login(self, event):
  24. '登录处理'
  25. try:
  26. serverAddress = self.serverAddress.GetLineText(0).split(':')
  27. con.open(serverAddress[0], port = int(serverAddress[1]), timeout = 10)
  28. response = con.read_some()
  29. if response != 'Connect Success':
  30. self.showDialog('Error', 'Connect Fail!', (95, 20))
  31. return
  32. con.write('login ' + str(self.userName.GetLineText(0)) + '\n')
  33. response = con.read_some()
  34. if response == 'UserName Empty':
  35. self.showDialog('Error', 'UserName Empty!', (135, 20))
  36. elif response == 'UserName Exist':
  37. self.showDialog('Error', 'UserName Exist!', (135, 20))
  38. else:
  39. self.Close()
  40. ChatFrame(None, -2, title = 'ShiYanLou Chat Client', size = (500, 350))
  41. except Exception:
  42. self.showDialog('Error', 'Connect Fail!', (95, 20))
  43. def showDialog(self, title, content, size):
  44. '显示错误信息对话框'
  45. dialog = wx.Dialog(self, title = title, size = size)
  46. dialog.Center()
  47. wx.StaticText(dialog, label = content)
  48. dialog.ShowModal()
  49. class ChatFrame(wx.Frame):
  50. """
  51. 聊天窗口
  52. """
  53. def __init__(self, parent, id, title, size):
  54. '初始化,添加控件并绑定事件'
  55. wx.Frame.__init__(self, parent, id, title)
  56. self.SetSize(size)
  57. self.Center()
  58. self.chatFrame = wx.TextCtrl(self, pos = (5, 5), size = (490, 310), style = wx.TE_MULTILINE | wx.TE_READONLY)
  59. self.message = wx.TextCtrl(self, pos = (5, 320), size = (300, 25))
  60. self.sendButton = wx.Button(self, label = "Send", pos = (310, 320), size = (58, 25))
  61. self.usersButton = wx.Button(self, label = "Users", pos = (373, 320), size = (58, 25))
  62. self.closeButton = wx.Button(self, label = "Close", pos = (436, 320), size = (58, 25))
  63. self.sendButton.Bind(wx.EVT_BUTTON, self.send)
  64. self.usersButton.Bind(wx.EVT_BUTTON, self.lookUsers)
  65. self.closeButton.Bind(wx.EVT_BUTTON, self.close)
  66. thread.start_new_thread(self.receive, ())
  67. self.Show()
  68. def send(self, event):
  69. '发送消息'
  70. message = str(self.message.GetLineText(0)).strip()
  71. if message != '':
  72. con.write('say ' + message + '\n')
  73. self.message.Clear()
  74. def lookUsers(self, event):
  75. '查看当前在线用户'
  76. con.write('look\n')
  77. def close(self, event):
  78. '关闭窗口'
  79. con.write('logout\n')
  80. con.close()
  81. self.Close()
  82. def receive(self):
  83. '接受服务器的消息'
  84. while True:
  85. sleep(0.6)
  86. result = con.read_very_eager()
  87. if result != '':
  88. self.chatFrame.AppendText(result)
  89. '程序运行'
  90. if __name__ == '__main__':
  91. app = wx.App()
  92. con = telnetlib.Telnet()
  93. LoginFrame(None, -1, title = "Login", size = (280, 200))
  94. app.MainLoop()

四、小结

最后就可以运行程序进行聊天了,注意需要先启动服务器再启动客户端。这个项目中使用了asyncore的dispatcher来实现服务器,asynchat的asyn_chat来维护用户的连接会话,用wxPython来实现图形界面,用telnetlib来连接服务器,在子线程中接受服务器发来的消息,由此一个简单的聊天室程序就完成了。

更多资源见123云盘,提取码:AQGl  https://www.123pan.com/s/f83gjv-nvWod

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/686412
推荐阅读
相关标签
  

闽ICP备14008679号