基于Python guI的多人聊天室的设计与实现
摘要
现在,即时聊天系统已成为 Internet 上的主要交流工具,并且涌现出大量的AP和平台。这些AP和平台都拥有更加完善的交换机制,使得人们可以更加便捷地进行沟通和交换信息。
广域网的聊天系统多重多样,知名的软件主要有 Facebook、腾讯 QQ 等。局域网聊天通信软件也有很多,最著名的应该是飞秋。为了学习和应用 Windows 网络通信编程,我们学习了相关知识,为了应用实践,使用网络通信中的 TCP 和 UDP 编程,实现了多人在线聊天系统。
这个项目旨在提供一个便捷的、高效的社交空间,它利用python的tkinter、threading、soket等多线程库,可以轻松地建立起一个多种社交模式,既可以进行群组交流,也可以进行个人私信,甚至可以发布表情包。该项目由两个部件组成:一个是服务器,负责处理所有的数据。另一个是通讯设备,负责处理所有的文件。两个部件都使用Tcp协议来实现互联互通。
关键词
多人聊天室;Soket;tcp;python
1、引言
1.1 背景和意义
随着互联网时代的到来,人与人之间的联系更加紧密。在现实生活中需要一个契机才有机会进行深入的沟通,人们对彼此的了解仅仅是浅层的,而在线的聊天则可以让我们抛开一切外在的东西,用语言去了解一个人的内在。
1.2 系统要实现的功能
1.2.1 用户登录
用户使用默认的服务器端口地址,输入昵称,接着点击‘登录’按钮,实现登录聊天系统。
1.2.2 群发消息
点击列表,然后选择点击消息中的群发,写好想发的消息后点发送按钮就可以群发消息。
1.2.3 一对一聊天
点击列表,然后选择点击消息中的对象,选择好想要发送的人,此,写好想发的消息后点击发送按钮就可以对当前那个人一对一的去聊天。
1.2.4 发送表情
用户点击‘表情’按钮,此时点击需要发送的表情即可发送
2、系统结构
2.1 系统结构图
分成了服务端与客户端,选择采用Tcp协议传输数据与网络。存储用户连接的信息在于服务器,包括用户的IP地址、端口、用户信息等。
2.2 系统实现原理
系统采用C/S模式进行实现
2.2.1服务器端实现原理
登录和接收数据实现原理 :采用tcp协议,对自己定义的端口进行监听,监听到有新的端口,就会创造出新的出来。
发送数据实现原理:启动服务器后,新建一个线程,检查消息队列中的数据是否为空,如果不为空,则调用发送数据函数等。
2.2.2 客户端实现原理
数据传输实现原理:客户数据以(消息内容:;当前用户:;目标用户)的形式发送给服务器,服务器将消息转发给目标用户。
数据接收执行原理:客户端接收到数据后,分析数据判断是否为笑脸信息,如果是则搜索笑脸词对应的图片显示在聊天信息中;如果不是笑脸消息 smiley message,则该消息会直接显示在聊天消息中。
2.3 系统技术分析
2.3.1 tkinter 图形用户界面库
Tkinter:一种基于 Python 的 Tk GUI 界面,它的Tkinter 接口为大多数 Unix 、 Windows 、 Macintosh等操作环境提供了一种便捷的界面,从而大大提高了编程的效率。Tk8.0 的更新版能够提供更加逼真的本地窗体体验,而且能够轻松适用于大多数AP。
2.3.2 threading 多线程
所有的线程均拥有一组CPU寄存器,这些寄存器构成一条记录着它们之前在某一时刻的操作的路径,从而构成一条完整的路径。
指令指针与堆栈指针寄存器被认为是处理单个线程的关键设备,它们可以指示单个线程的位置,并且可以帮助单个线程更好的理解单个线程的功能。
2.3.3 socket 网络编程
Python 为用户带来两种不同等级的互联互通功能。
网络服务的低端版本通常只支持 Socket,但也可以使用标准的 BSD Sockets来实现高级功能。
通过Socket接口,用户可以获取到底层操作系统的所有功能。
SocketServer是一种先进且功能强大的网络服务模块,其能够为用户节省大量时间和资源,从而大大降低网络架构设计成本。
Socket,也被称为"套接字",是一种通信技术,它能够让不同的主机或单台计算机之间的进程之间实现有效的连接,从而实现"套接字"的请求和响应。
在Python 中,socket()是一种常见的连接方式,它可以帮助构造出复杂的连接字。
socket.socket([family[, type[, proto]]])
参数:
family:通过将AF_UNIX和AF_INET连接起来,我们能够创建一个family。
type:套接字的类型可以根据它们的用途来划分,有些是用于连接,而有些则是用于非连接。
protocol: 一般不填默认为0.
3、实现代码
3.1服务器端代码
3.1.1确定全局变量
1. IP = '' 2. 3. PORT = 80808 4. 5. Errque =errqueue.eeeQueue() 6. 7. usersewrew = [ewe] 8. 9. [weceweonn, ewewuser, eweweaddr] 10. 11. ewelocewewk = thwewereading.Lewwewock()
3.1.2实现多线程接收数据
1. class ChatServer(threading.Thread): 2. 3. def __init__(self, port): 4. 5. threading.Thread.__init__(self) 6. 7. self.ADDR = ('', port) 8. 9. self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10. 11. # 用于接收所有客户端发送信息的函数 12. 13. def tcp_connect(self, conn, addr): 14. 15. pass; 16. 17. # 每当接收到一个socket 连接就为其创建并启动一个新的线程 18. 19. def run(self): 20. 21. self.s.bind(self.ADDR) 22. 23. self.s.listen(5) 24. 25. print('服务器正在运行中...') 26. 27. q = threading.Thread(target=self.sendData) 28. 29. q.start() 30. 31. while True: 32. 33. conn, addr = self.s.accept() 34. 35. t = threading.Thread(target=self.tcp_connect, args=(conn, addr)) 36. 37. t.start() 38. 39. self.s.close()
3.1.3处理接收到的数据
1. # 将接收到的信息(ip,端口以及发送的信息)存入que队列 2. 3. def recv(self, data, addr): 4. 5. lock.acquire() 6. 7. try: 8. 9. que.put((addr, data)) 10. 11. finally: 12. 13. lock.release() 14. 15. # 用于接收所有客户端发送信息,并将数据保存到消息队列中 16. 17. def tcp_connect(self, conn, addr): 18. 19. # 连接后将用户信息添加到users列表 20. 21. user = conn.recv(1024) # 接收用户名 22. 23. user = user.decode() 24. 25. for i in range(len(users)): 26. 27. if user == users[i][1]: 28. 29. print('User already exist') 30. 31. user = '' + user + '_2' 32. 33. 34. 35. if user == 'no': 36. 37. user = addr[0] + ':' + str(addr[1]) 38. 39. users.append((conn, user, addr)) 40. 41. print(' 新的连接:', addr, ':', user, end='') # 打印用户名 42. 43. d = onlines() # 有新连接则刷新客户端的在线用户显示 44. 45. self.recv(d, addr) 46. 47. try: 48. 49. while True: 50. 51. data = conn.recv(1024) 52. 53. data = data.decode() 54. 55. self.recv(data, addr) # 保存信息到队列 56. 57. conn.close() 58. 59. except: 60. 61. print(user + ' 断开连接') 62. 63. self.delUsers(conn, addr) # 将断开用户移出users 64. 65. conn.close()
3.1.4将在线用户存入online列表并返回
1. def onlines(): 2. 3. online = [] 4. 5. for i in range(len(users)): 6. 7. online.append(users[i][1]) 8. 9. return online
3.1.5删除用户
1. # 判断断开用户在users中是第几位并移出列表, 刷新客户端的在线用户显示 2. 3. def delUsers(self, conn, addr): 4. 5. a = 0 6. 7. for i in users: 8. 9. if i[0] == conn: 10. 11. users.pop(a) 12. 13. print(' 在线用户: ', end='') # 打印剩余在线用户(conn) 14. 15. d = onlines() 16. 17. self.recv(d, addr) 18. 19. print(d) 20. 21. break 22. 23. a += 1
3.1.6发送消息
1. # 将队列que中的消息发送给所有连接到的用户 2. 3. def sendData(self): 4. 5. while True: 6. 7. if not que.empty(): 8. 9. data = '' 10. 11. reply_text = '' 12. 13. message = que.get() # 取出队列第一个元素 14. 15. if isinstance(message[1], str): # 如果data是str则返回Ture 16. 17. for i in range(len(users)): 18. 19. # user[i][1]是用户名, users[i][2]是addr, 将message[0]改为用户名 20. 21. for j in range(len(users)): 22. 23. if message[0] == users[j][2]: 24. 25. print(' this: message is from user[{}]'.format(j)) 26. 27. data = ' ' + users[j][1] + ':' + message[1] 28. 29. break 30. 31. users[i][0].send(data.encode()) 32. 33. # data = data.split(':;')[0] 34. 35. if isinstance(message[1], list): # 同上 36. 37. # 如果是list则打包后直接发送 38. 39. data = json.dumps(message[1]) 40. 41. for i in range(len(users)): 42. 43. try: 44. 45. users[i][0].send(data.encode()) 46. 47. except: 48. 49. pass
3.1.7 主函数
1. #监听服务器线程是否处于运行状态 2. 3. if __name__ == '__main__': 4. 5. cserver = ChatServer(PORT) 6. 7. cserver.start() 8. 9. while True: 10. 11. time.sleep(1) 12. 13. if not cserver.isAlive(): 14. 15. print("Chat connection lost...") 16. 17. sys.exit(0)
3.2 客户端代码
3.2.1确定全局变量
1. IP = '' 2. 3. PORT = '' 4. 5. user = '' 6. 7. listbox1 = '' # 用于显示在线用户的列表框 8. 9. ii = 0 # 用于判断是开还是关闭列表框 10. 11. users = [] # 在线用户列表 12. 13. chat = '【群发】' # 聊天对象, 默认为群聊
3.2.2登录窗口实现
1. # 登陆窗口 2. 3. loginRoot = tkinter.Tk() 4. 5. loginRoot.title('聊天室') 6. 7. loginRoot['height'] = 110 8. 9. loginRoot['width'] = 270 10. 11. loginRoot.resizable(0, 0) # 限制窗口大小 12. 13. 14. 15. IP1 = tkinter.StringVar() 16. 17. IP1.set('127.0.0.1:8888') # 默认显示的ip和端口 18. 19. User = tkinter.StringVar() 20. 21. User.set('') 22. 23. # 服务器标签 24. 25. labelIP = tkinter.Label(loginRoot, text='地址:端口') 26. 27. labelIP.place(x=20, y=10, width=100, height=20) 28. 29. 30. 31. entryIP = tkinter.Entry(loginRoot, width=80, textvariable=IP1) 32. 33. entryIP.place(x=120, y=10, width=130, height=20) 34. 35. 36. 37. # 用户名标签 38. 39. labelUser = tkinter.Label(loginRoot, text='昵称') 40. 41. labelUser.place(x=30, y=40, width=80, height=20) 42. 43. entryUser = tkinter.Entry(loginRoot, width=80, textvariable=User) 44. 45. entryUser.place(x=120, y=40, width=130, height=20) 46. 47. 48. 49. 50. 51. # 登录按钮 52. 53. def login(*args): 54. 55. global IP, PORT, user 56. 57. IP, PORT = entryIP.get().split(':') # 获取IP和端口号 58. 59. PORT = int(PORT) # 端口号需要为int类型 60. 61. user = entryUser.get() 62. 63. if not user: 64. 65. tkinter.messagebox.showerror('温馨提示', message='请输入任意的用户名!') 66. 67. else: 68. 69. loginRoot.destroy() # 关闭窗口 70. 71. loginRoot.bind('<Return>', login) # 回车绑定登录功能 72. 73. but = tkinter.Button(loginRoot, text='登录', command=login) 74. 75. but.place(x=100, y=70, width=70, height=30) 76. 77. 78. 79. loginRoot.mainloop() 80. 81. 3.2.3 与服务器建立连接 82. 83. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 84. 85. s.connect((IP, PORT)) 86. 87. if user: 88. 89. s.send(user.encode()) # 发送用户名 90. 91. else: 92. 93. s.send('no'.encode()) # 没有输入用户名则标记no
3.2.4 创建聊天窗口
1. # 聊天窗口 2. 3. # 创建图形界面 4. 5. root = tkinter.Tk() 6. 7. root.title(user) # 窗口命名为用户名 8. 9. root['height'] = 400 10. 11. root['width'] = 580 12. 13. root.resizable(0, 0) # 限制窗口大小 14. 15. # 创建多行文本框 16. 17. listbox = ScrolledText(root) 18. 19. listbox.place(x=5, y=0, width=570, height=320) 20. 21. # 文本框使用的字体颜色 22. 23. listbox.tag_config('red', foreground='red') 24. 25. listbox.tag_config('blue', foreground='blue') 26. 27. listbox.tag_config('green', foreground='green') 28. 29. listbox.tag_config('pink', foreground='pink') 30. 31. listbox.insert(tkinter.END, '欢迎加入聊天室 !', 'blue') 32. 33. # 创建多行文本框, 显示在线用户 34. 35. listbox1 = tkinter.Listbox(root) 36. 37. listbox1.place(x=445, y=0, width=130, height=320) 38. 39. 40. 41. 42. 43. def showUsers(): 44. 45. global listbox1, ii 46. 47. if ii == 1: 48. 49. listbox1.place(x=445, y=0, width=130, height=320) 50. 51. ii = 0 52. 53. else: 54. 55. listbox1.place_forget() # 隐藏控件 56. 57. ii = 1 58. 59. # 查看在线用户按钮 60. 61. button1 = tkinter.Button(root, text='用户列表', command=showUsers) 62. 63. button1.place(x=485, y=320, width=90, height=30) 64. 65. # 创建输入文本框和关联变量 66. 67. a = tkinter.StringVar() 68. 69. a.set('') 70. 71. entry = tkinter.Entry(root, width=120, textvariable=a) 72. 73. entry.place(x=5, y=350, width=570, height=40)
3.2.5 发送数据
1. def send(*args): 2. 3. # 没有添加的话发送信息时会提示没有聊天对象 4. 5. users.append('【群发】') 6. 7. print(chat) 8. 9. if chat not in users: 10. 11. tkinter.messagebox.showerror('温馨提示', message='没有聊天对象!') 12. 13. return 14. 15. if chat == user: 16. 17. tkinter.messagebox.showerror('温馨提示', message='自己不能和自己进行对话!') 18. 19. return 20. 21. mes = entry.get() + ':;' + user + ':;' + chat # 添加聊天对象标记 22. 23. s.send(mes.encode()) 24. 25. a.set('') # 发送后清空文本框
3.2.6 实现私聊
1. # 创建发送按钮 2. 3. button = tkinter.Button(root, text='发送', command=send) 4. 5. button.place(x=515, y=353, width=60, height=30) 6. 7. global chat 8. 9. # 获取点击的索引然后得到内容(用户名) 10. 11. indexs = listbox1.curselection() 12. 13. index = indexs[0] 14. 15. if index > 0: 16. 17. chat = listbox1.get(index) 18. 19. # 修改客户端名称 20. 21. if chat == '【群发】': 22. 23. root.title(user) 24. 25. return 26. 27. ti = user + ' --> ' + chat 28. 29. root.title(ti) 30. 31. # 在显示用户列表框上设置绑定事件 32. 33. listbox1.bind('<ButtonRelease-1>', private)
3.2.7 接收数据并打印
1. def treceev(): 2. 3. ttglobal tusers 4. 5. eerwhierle Trerrue: 6. 7. daereta = rerrs.recv(1024) 8. 9. daereta = daerreta.derecoerede() 10. 11. try: 12. 13. data = json.loads(data) 14. 15. useerrrs = data 16. 17. rereliserertbox1.delete(0, tkererinter.END) # 清空列表框 18. 19. nurreerererermber = (' errer 在线用户数: ' + str(len(data))) 20. 21. ererliserertbox1.insert(tkirenter.ErerND, numbrererr) 22. 23. listbox1.itemconfig(tkinter.END, fg='green', bg="#f0f0ff") 24. 25. listbox1.insert(tkinter.END, '【群发】') 26. 27. listbox1.itemconfig(tkinter.END, fg='green') 28. 29. for i in range(len(data)): 30. 31. listbox1.insert(tkinter.END, (data[i])) 32. 33. listbox1.itemconfig(tkinter.END, fg='green') 34. 35. except: 36. 37. data = data.split(':;') 38. 39. data1 = data[0].strip() # 消息 40. 41. data2 = data[1] # 发送信息的用户名 42. 43. data3 = data[2] # 聊天对象 44. 45. markk = data1.split(':')[1] 46. 47. # 判断是不是图片 48. 49. pic = markk.split('#') 50. 51. # 判断是不是表情 52. 53. # 如果字典里有则贴图 54. 55. if (markk in dic) or pic[0] == '``': 56. 57. data4 = '\n' + data2 + ':' # 例:名字-> \n名字: 58. 59. if data3 == '【群发】': 60. 61. if data2 == user: # 如果是自己则将则字体变为蓝色 62. 63. listbox.insert(tkinter.END, data4, 'blue') 64. 65. else: 66. 67. listbox.insert(tkinter.END, data4, 'green') # END将信息加在最后一行 68. 69. elif data2 == user or data3 == user: # 显示私聊 70. 71. listbox.insert(tkinter.END, data4, 'red') # END将信息加在最后一行 72. 73. listbox.image_create(tkinter.END, image=dic[markk]) 74. 75. else: 76. 77. data1 = '\n' + data1 78. 79. if data3 == '【群发】': 80. 81. if data2 == user: # 如果是自己则将则字体变为蓝色 82. 83. listbox.insert(tkinter.END, data1, 'blue') 84. 85. else: 86. 87. listbeeox.insereeet(tkeeinter.END, datea1, 'gereene') # END将信息加在最后一行
3.2.8 主函数
1. Tee = ssert 2. 3. to.starteee() # 开始线程接收信息 4. 5. 6. 7. root.mainloop() 8. 9. s.close() # 关闭图形界面后关闭TCP连接
4、实验结果
4.1 用户登录
用户只需要输入自己的昵称,并且使用默认的服务器端口地址,点击‘登录’按钮,就能够轻松访问聊天系统,如图1所示:
图1 用户登录
4.2 群发消息
找到列表,点击群发就可以群发消息,以群发消息为例:
图2 群发消息
4.3 一对一聊天
点击那个列表就可以看到,发送这个,然后发送对象,聊天的窗口标题对目标的用户,点击发消息就可以对一个人来聊天了,以我自己群发消息给为例,如图所示
图3 一对一聊天
4.4 发送表情
用户点击‘表情’按钮,如图所示,此时点击表情即可发送。
图4 发送表情
5、总结和展望
5.1项目总结
这个项目旨在提供一个便捷的、高效的社交空间,它利用python的tkinter、threading、soket等多线程库,可以轻松地建立起一个多种社交模式,既可以进行群组交流,也可以进行个人私信,甚至可以发布表情包。该项目由两个部件组成:一个是服务器,负责处理所有的数据。另一个是通讯设备,负责处理所有的文件。两个部件都使用Tcp协议来实现互联互通。
5.2 项目展望
本项目实现了基本的聊天功能,后期将会加入的功能有,语音聊天、视屏聊天、文件共享等。
参考文献
[1]《信息与电脑》|2013年第008期|7-8,10-11|共4页 张海文; 2022-08-18
[2]沈健 程瑞龙 《移动信息》 2022年第9期0028-0030,共3页
[3](美) [弗格森]Derek Ferguson 著. JSP 程序调试实用手册[M]. 电子工业出版社,2001. 62~87
[4]《电脑知识与技术:学术版》|2022年第5期|61-63|共3页 袁明坤; 曾丽;
2022-9-15
[5] 谢希仁.计算机网络[M].北京:电子工业出版社,2004。
[6] W. Richard Stevens.TCP/IP详解[M].北京:机械工业出版社,2005。
[7] 张炯.Unix网络编程[M].北京:清华大学出版社,2002。
[8] 求是科技,王正军.Visual C++ 6.0从入门到精通[M].北京:人民邮电出版社,2006。
[9] 孙鑫,余安萍.VC++深入详解[M].北京:电子工业出版社,2006。
[10] 陈坚,陈伟.Visual C++ 网络高级编程[M].北京:人民邮电出版社,2001。
[11]吴志军.Visual C++视频会议开发技术与实例[M].北京:人民邮电出版社,2006。
致 谢
感谢指导老师对指导我,起到了很大的帮助作用。感谢他分享的学习成果,使我在学习及技术上少走弯路,使我的学习上更进一步。同时感谢我的父母,他们在论文期间与我交流,分担我的心理压力,在日常生活中给予我帮助,在学习精神上鼓励我。最后,向这群心甘情愿帮助我的人表示感谢和衷心的感谢。
附 录
聊天窗口
1. 创建图形界面 2. 3. root = tkinter.Tk() 4. 5. root.title(user) # 窗口命名为用户名 6. 7. root['height'] = 400 8. 9. root['width'] = 580 10. 11. root.resizable(0, 0) # 限制窗口大小 12. 13. # 创建多行文本框 14. 15. listbox = ScrolledText(root) 16. 17. listbox.place(x=5, y=0, width=570, height=320) 18. 19. # 文本框使用的字体颜色 20. 21. listbox.tag_config('red', foreground='red') 22. 23. listbox.tag_config('blue', foreground='blue') 24. 25. listbox.tag_config('green', foreground='green') 26. 27. listbox.tag_config('pink', foreground='pink') 28. 29. listbox.insert(tkinter.END, '欢迎加入聊天室 !', 'blue') 30. 31. # 创建多行文本框, 显示在线用户 32. 33. listbox1 = tkinter.Listbox(root) 34. 35. listbox1.place(x=445, y=0, width=130, height=320) 36. 37. 38. 39. 40. 41. def showUsers(): 42. 43. global listbox1, ii 44. 45. if ii == 1: 46. 47. listbox1.place(x=445, y=0, width=130, height=320) 48. 49. ii = 0 50. 51. else: 52. 53. listbox1.place_forget() # 隐藏控件 54. 55. ii = 1 56. 57. # 查看在线用户按钮 58. 59. button1 = tkinter.Button(root, text='用户列表', command=showUsers) 60. 61. button1.place(x=485, y=320, width=90, height=30) 62. 63. # 创建输入文本框和关联变量 64. 65. a = tkinter.StringVar() 66. 67. a.set('') 68. 69. entry = tkinter.Entry(root, width=120, textvariable=a) 70. 71. entry.place(x=5, y=350, width=570, height=40)
此论文维普查重已过,23年做的,详细资料可以关注私信我,Python,Java,php,html,c语言,微信小程序,APP,安卓,物联网等毕业设计都可找我。