300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > python多线程+socket实现聊天室(最终版)

python多线程+socket实现聊天室(最终版)

时间:2021-08-13 10:11:49

相关推荐

python多线程+socket实现聊天室(最终版)

文章目录

一. 成果预览二.源码分享 总结

gif动图中的内容bug已被修改,如下项目结构是最新版本内容,代码已更新

一. 成果预览

二.源码分享

目录结构

main.py

from src import LoginUserif __name__ == "__main__":# 程序启动 用户信息校验LoginUser.LoginUser()

LoginUser.py

import jsonimport threadingfrom multiprocessing import Processfrom tkinter import *from tkinter import messageboxfrom PIL import Image, ImageTkfrom src import Server, Clientuser_tempList = []class LoginUser:def __init__(self):self.root = Tk()self.root.title("系统登录")self.root.geometry("400x300")# 画布放置图片img = Image.open('../static/1.png')photo = ImageTk.PhotoImage(img)Label(self.root, image=photo).grid(row=0, column=0)# 标签 用户名密码Label(self.root, text='用户名:').place(x=100, y=120)Label(self.root, text='密码:').place(x=100, y=160)# 用户名输入框self.var_username = StringVar()self.entry_userame = Entry(self.root, textvariable=self.var_username, font=("Arial", 10))self.entry_userame.place(x=160, y=120)# 密码输入框self.var_password = StringVar()self.entry_password = Entry(self.root, textvariable=self.var_password, font=("Arial", 10))self.entry_password.place(x=160, y=160)# 读取用户数据self.readUserData()# 登录按钮bt_login = Button(self.root, text='登录', command=self.login)bt_login.place(x=160, y=230)# 注册按钮bt_login = Button(self.root, text='注册', command=self.writeUserData)bt_login.place(x=200, y=230)self.root.mainloop()# 验证逻辑def login(self):# 输入框获取用户名密码usr_name = self.var_username.get()usr_pwd = self.var_password.get()flag = False# 查库for user in user_tempList:if usr_name == user.get('username') and usr_pwd == user.get('password'):flag = True# 如果库中数据和用户输入匹配。登陆成功,打开用户端和服务端窗口if flag:login_success = "登录成功"messagebox.showinfo(message=login_success)self.root.destroy() # 摧毁窗口# 开启线程运行服务端threading.Thread(target=Server.Server).start()# 额外进程运行客户端Process(target=Client.Client).start()# 查库失败,给出提示信息else:messagebox.showinfo(message="账号或密码错误!")self.entry_userame.delete('0', 'end')self.entry_userame.focus_set()self.entry_password.delete('0', 'end')# 读取用户数据def readUserData(self):try:with open('../data/loginData.json', 'r', encoding='utf-8') as f:# json加载文件内容data = json.load(f)# 如果json有内容if data is not None or data != '':for i in range(len(data)):username = data[i]['username']password = data[i]['password']user_tempList.append({'username': username, 'password': password})except:messagebox.showerror(message="读取用户数据库失败")# 将用户注册信息落库def writeUserData(self):# 追加新用户username = self.var_username.get()password = self.var_password.get()if username != "" and password != "":user_tempList.append({'username': username, 'password': password})try:with open('../data/loginData.json', 'w', encoding='utf-8') as f:# 写入临时json列表的内容json.dump(user_tempList, f, ensure_ascii=False)except:messagebox.showerror(message="写入用户信息记录失败")messagebox.showinfo(message="用户信息注册成功")# 更新用户数据self.readUserData()if __name__ == "__main__":LoginUser()# LoginUser.readUserData()# LoginUser.writeUserData()# LoginUser.login()

server.py

import jsonimport tkinter.messageboxfrom tkinter import *from PIL import Image, ImageTk, ImageSequenceimport timeimport socketimport sysimport threading# 临时存储读的json数据json_temp = []class Server:# 初始化创建socket套接字def __init__(self):# 获取tcp/ip套接字self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定(主机,端口号)到套接字self.server.bind(('localhost', 8081))# 开始TCP监听self.server.listen(5)# 被动接受TCP客户的连接,(阻塞式)等待连接的到来self.client, self.addr = self.server.accept()# 调用GUI界面self.view()# 析构函数,当程序退出时,执行该函数,主要是为了关闭创建的socket套接字def __del__(self):# 关闭套接字self.server.close()self.client.close()# GUI界面绘制def view(self):# 初始化win窗口self.init = Tk()# 窗口标题self.init.title("服务端聊天工具")# 创建frame容器self.frmLT = Frame(width=500, height=280)self.frmLC = Frame(width=500, height=100)self.frmLB = Frame(width=500, height=30)self.frmRT = Frame(width=300, height=500)self.frmT = Frame(width=500, height=20)# 创建聊天框标题self.w = Label(self.init, text="服务端信息框", width=60)self.w.place(x=20, y=20)# 聊天框消息列表self.txtMsgList = Text(self.frmLT)self.txtMsgList.tag_config('green-color', foreground='#008C00') # 创建tag# 按钮self.btnSend = Button(self.frmLB, text="发送", width=8, command=self.insertMsg)self.btnCancel = Button(self.frmLB, text='取消', width=8, command=self.cancelMsg)# 发送的消息文本self.txtMsg = Text(self.frmLC)# 右边栏图片背景设置self.imgInfo = tkinter.PhotoImage(file="../static/渊洁.gif")self.lblImage = tkinter.Label(self.frmRT, image=self.imgInfo, width=300)self.lblImage.image = self.imgInfo# 定时任务监听线程连接,获取消息self.init.after(1000, self.getListenSocket) # 这里调用了after方法,即隔一段时间执行事件# 窗口布局self.frmT.grid(row=0, column=0, padx=1, pady=3)self.frmLT.grid(row=1, column=0, columnspan=2, padx=1, pady=2)self.frmLC.grid(row=2, column=0, columnspan=2, padx=1, pady=3)self.frmLB.grid(row=3, column=0, columnspan=2)self.frmRT.grid(row=1, column=2, rowspan=3, padx=2, pady=3)# 固定大小self.frmT.grid_propagate(0)self.frmLT.grid_propagate(0)self.frmLC.grid_propagate(0)self.frmLB.grid_propagate(0)self.frmRT.grid_propagate(0)# 按钮布局self.btnSend.grid(row=2, column=0)self.btnCancel.grid(row=2, column=1)self.txtMsgList.grid()self.txtMsg.grid()self.lblImage.grid()# 读记录self.readChatRecord()# 循环调用窗口self.init.mainloop()# 清屏消息def cancelMsg(self): # 取消信息self.txtMsgList.delete('0.0', END)'''创建线程对象threading.Thread()target执行运行的函数'''def getListenSocket(self):t = threading.Thread(target=self.getSocketMsg)# 设置为守护线程t.setDaemon(True)# 开启线程t.start()# 回调监听,更新消息 这里与上面的after方法一致,这样就能每隔一段时间执行self.init.after(1000, self.getListenSocket)'''此函数是为了判断对方有没有发送消息,如果有,就在文本框显示对方发送的消息'''def getSocketMsg(self):msg = self.client.recv(1024)if msg is not None:msg = msg.decode('utf-8')strMsg = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n'self.txtMsgList.insert(END, strMsg, 'green-color')self.txtMsgList.insert(END, "客户端发来的消息:" + msg + "\n")# 数据落库,写入json文件self.writeFile(strMsg, "客户端发来的消息:" + msg+ "\n")# 将输入内容放入消息列表def insertMsg(self): # 这是按钮事件,即button点击事件,点击后,发送文本框消息sendMsg = self.txtMsg.get(1.0, END)sendMsg1 = sendMsgself.client.send(sendMsg1.encode('utf-8'))sendTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n'self.txtMsgList.insert(END, sendTime, 'green-color')self.txtMsgList.insert(END, sendMsg + "\n")# 数据落库,写入json文件self.writeFile(sendTime, "你发送的消息:" + sendMsg)# 清除输入框的消息内容self.txtMsg.delete(1.0, END)# 读取json记录文件消息def readChatRecord(self):try:with open('../data/chatRecordServer.json', 'r', encoding='utf-8') as f:# json加载文件内容json_data = json.load(f)# 如果json有内容if json_data is not None or json_data != '':for i in range(len(json_data)):sendTime = json_data[i]['sendTime']sendMsg = json_data[i]['sendMsg']# 将json中保留的数据放入消息列表中self.txtMsgList.insert(END, sendTime, 'green-color')self.txtMsgList.insert(END, sendMsg + "\n")# 临时变量保留读取的json数据,之后方便追加写入json_temp.append({'sendTime': sendTime, 'sendMsg': sendMsg})except:tkinter.messagebox.showerror(message="读取历史记录失败")# 写入json记录文件消息def writeFile(self, sendTime, sendMsg):# 追加新消息json_temp.append({'sendTime': sendTime, 'sendMsg': sendMsg})try:with open('../data/chatRecordServer.json', 'w', encoding='utf-8') as f:# 写入临时json列表的内容json.dump(json_temp, f, ensure_ascii=False)f.flush()except:tkinter.messagebox.showerror(message="写入历史记录失败")if __name__ == "__main__":Server()

client.py

import jsonimport tkinter.messageboxfrom tkinter import *import cv2import numpyfrom PIL import Image, ImageTk, ImageSequenceimport timeimport socketimport sysimport threading# 临时存储读的json数据,在此基础做追加写入json_temp = []class Client:# 初始化创建socket套接字def __init__(self):# 获取tcp/ip套接字'''family:socket.AF_INET:指定通过IPV4进行连接socket.AF_INET6:指定通过IPV6进行通信type :socket.SOCK_STREAM:通过TCP进行通信socket.SOCK_DGRAM: 通过udp进行通信'''self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定(主机,端口号)到套接字self.client.connect(('localhost', 8081))# 调用GUI界面self.view()def __del__(self):# 关闭套接字self.client.close()# GUI界面绘制def view(self):# 初始化win窗口self.init = Tk()# 窗口标题self.init.title("客户端聊天工具")# 创建frame容器self.frmLT = Frame(width=500, height=280)self.frmLC = Frame(width=500, height=100)self.frmLB = Frame(width=500, height=30)self.frmRT = Frame(width=300, height=500)self.frmT = Frame(width=500, height=20)# 创建聊天框标题self.w = Label(self.init, text="客户端信息框", width=60)self.w.place(x=20, y=20)# 聊天框消息列表self.txtMsgList = Text(self.frmLT)self.txtMsgList.tag_config('green-color', foreground='#008C00') # 创建tag# 按钮self.btnSend = Button(self.frmLB, text="发送", width=8, command=self.insertMsg)self.btnCancel = Button(self.frmLB, text='取消', width=8, command=self.cancelMsg)# 发送的消息文本self.txtMsg = Text(self.frmLC)# 右边栏图片背景设置self.imgInfo = tkinter.PhotoImage(file="../static/1.gif")self.lblImage = tkinter.Label(self.frmRT, image=self.imgInfo, width=300)self.lblImage.image = self.imgInfo# 开启gif任务thr = threading.Thread(target=self.gif)thr.setDaemon(True)thr.start()# 定时任务监听线程连接,获取消息self.init.after(1000, self.getListenSocket) # 这里调用了after方法,即隔一段时间执行事件# 窗口布局self.frmT.grid(row=0, column=0, padx=1, pady=3)self.frmLT.grid(row=1, column=0, columnspan=2, padx=1, pady=3)self.frmLC.grid(row=2, column=0, columnspan=2, padx=1, pady=3)self.frmLB.grid(row=3, column=0, columnspan=2)self.frmRT.grid(row=1, column=2, rowspan=3, padx=2, pady=3)# 固定大小self.frmT.grid_propagate(0)self.frmLT.grid_propagate(0)self.frmLC.grid_propagate(0)self.frmLB.grid_propagate(0)self.frmRT.grid_propagate(0)self.btnSend.grid(row=2, column=0)self.btnCancel.grid(row=2, column=1)self.txtMsgList.grid()self.txtMsg.grid()self.lblImage.grid()# 读记录self.readChatRecord()# 循环调用窗口self.init.mainloop()# 清屏消息def cancelMsg(self): # 取消信息self.txtMsgList.delete('0.0', END)# gif动图def gif(self):pic_name = "../static/1.gif"im = Image.open(pic_name)while True:for frame in ImageSequence.Iterator(im): # 使用迭代器frame = frame.convert('RGB')cv2_frame = numpy.array(frame)show_frame = cv2.cvtColor(cv2_frame, cv2.COLOR_RGB2BGR)cv2.imshow(pic_name, show_frame)cv2.waitKey(250)'''创建线程对象threading.Thread()target执行运行的函数'''def getListenSocket(self):t = threading.Thread(target=self.getSocketMsg)# 设置为守护线程t.setDaemon(True)# 开启线程t.start()# 回调监听,更新消息self.init.after(1000, self.getListenSocket)'''此函数是为了判断对方有没有发送消息,如果有,就在文本框显示对方发送的消息'''def getSocketMsg(self):msg = self.client.recv(1024)if msg is not None:msg = msg.decode('utf-8')strMsg = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n'self.txtMsgList.insert(END, strMsg, 'green-color')self.txtMsgList.insert(END, "服务端发来的消息:" + msg + "\n")# 数据落库,写入json文件self.writeFile(strMsg, "服务端发来的消息:" + msg+ "\n")# 将输入内容放入消息列表def insertMsg(self):sendMsg = self.txtMsg.get(1.0, END)sendMsg1 = sendMsgself.client.send(sendMsg1.encode('utf-8'))sendTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '\n'self.txtMsgList.insert(END, sendTime, 'green-color')self.txtMsgList.insert(END, sendMsg + "\n")# 数据落库,写入json文件self.writeFile(sendTime, "你发送的的消息:" + sendMsg)# 清除输入框的消息内容self.txtMsg.delete(1.0, END)# 读取json记录文件消息def readChatRecord(self):try:with open('../data/chatRecordClient.json', 'r', encoding='utf-8') as f:# json加载文件内容json_data = json.load(f)# 如果json有内容if json_data is not None or json_data != '':for i in range(len(json_data)):sendTime = json_data[i]['sendTime']sendMsg = json_data[i]['sendMsg']# 将json中保留的数据放入消息列表中self.txtMsgList.insert(END, sendTime, 'green-color')self.txtMsgList.insert(END, sendMsg + "\n")# 临时变量保留读取的json数据,之后方便追加写入json_temp.append({'sendTime': sendTime, 'sendMsg': sendMsg})except:tkinter.messagebox.showerror(message="读取历史记录失败")# 写入json记录文件消息def writeFile(self, sendTime, sendMsg):# 追加新消息json_temp.append({'sendTime': sendTime, 'sendMsg': sendMsg})try:with open('../data/chatRecordClient.json', 'w', encoding='utf-8') as f:# 写入临时json列表的内容json.dump(json_temp, f, ensure_ascii=False)f.flush()except:tkinter.messagebox.showerror(message="写入历史记录失败")if __name__ == "__main__":Client()

总结

1.本人主攻java开发。对于py存储json的操作很不适应,不是面向对象的写法。而是无限套娃。。。我并没有套娃,所以你会发现json格式很奇怪。

2.上述程序还有很多值得优化的地方。比如开窗体我额外开了一个进程。因为我试了好多种方法。多线程都无法在引用目标文件的实例对象中加载多个。

3. 程序中的IO流操作需要急切的优化,程序中写操作是读取文件的所有信息,之后暂存列表等待追加。最后覆盖原文件。可以考虑只追加到文件末。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。