python实现多人聊天室


Posted in Python onMarch 31, 2020

本文实例为大家分享了python实现多人聊天室的具体代码,供大家参考,具体内容如下

一、目的

以实现小项目的方式,来巩固之前学过的Python基本语法以及相关的知识。 

二、相关技术

1.wxpython GUI编程

2.网络编程

3.多线程编程

4.数据库编程

5.简单的将数据导出到Excel表 

三、存在的漏洞以及不足

1.由于数据库编码的问题,无法使用中文。

2.在客户端关闭后,其相关的线程仍然存在于服务器的用户线程队列中,所以服务器会错误地往已关闭的客户端传送信息。

3.客户端初始登录并加载历史记录时,会出现每条历史消息后面的回车键丢失的现象,解决的方法是:在加载相邻两条消息之间加个时间间隔,但效果不佳。

四、源码

服务器Server:

# -*- coding: UTF-8 -*-

from socket import *
import time
import threading
import wx
import MySQLdb
import xlwt
from clientthread import ClientThread

class Server(wx.Frame):
 def __init__(self,parent=None,id=-1,title='服务器',pos=wx.DefaultPosition,size=(500,300)):

 '''窗口'''
 wx.Frame.__init__(self,parent,id,title,pos,size=(400,470))
 pl = wx.Panel(self)
 con = wx.BoxSizer(wx.VERTICAL)
 subcon = wx.FlexGridSizer(wx.HORIZONTAL)
 sta = wx.Button(pl , size=(133, 40),label='启动服务器')
 end = wx.Button(pl, size=(133, 40), label='关闭服务器')
 hist = wx.Button(pl,size=(133,40),label='导出聊天记录')
 subcon.Add(sta, 1, wx.BOTTOM)
 subcon.Add(hist, 1, wx.BOTTOM)
 subcon.Add(end, 1, wx.BOTTOM)
 con.Add(subcon,1,wx.ALIGN_CENTRE|wx.BOTTOM)
 self.Text = wx.TextCtrl(pl, size=(400,250),style = wx.TE_MULTILINE|wx.TE_READONLY)
 con.Add(self.Text, 1, wx.ALIGN_CENTRE)
 self.ttex = wx.TextCtrl(pl, size=(400,100),style=wx.TE_MULTILINE)
 con.Add(self.ttex, 1, wx.ALIGN_CENTRE)
 sub2 = wx.FlexGridSizer(wx.HORIZONTAL)
 clear = wx.Button(pl, size=(200, 40), label='清空')
 send = wx.Button(pl, size=(200, 40), label='发送')
 sub2.Add(clear, 1, wx.TOP | wx.LEFT)
 sub2.Add(send, 1, wx.TOP | wx.RIGHT)
 con.Add(sub2, 1, wx.ALIGN_CENTRE)
 pl.SetSizer(con)
 '''窗口'''

 '''绑定'''
 self.Bind(wx.EVT_BUTTON, self.EditClear, clear)
 self.Bind(wx.EVT_BUTTON, self.SendMessage, send)
 self.Bind(wx.EVT_BUTTON, self.Start, sta)
 self.Bind(wx.EVT_BUTTON, self.Break, end)
 self.Bind(wx.EVT_BUTTON, self.WriteToExcel, hist)
 '''绑定'''

 '''服务器准备工作'''
 self.UserThreadList = []
 self.onServe = False
 addr = ('', 21567)
 self.ServeSock = socket(AF_INET, SOCK_STREAM)
 self.ServeSock.bind(addr)
 self.ServeSock.listen(10)
 '''服务器准备工作'''

 '''数据库准备工作,用于存储聊天记录'''
 self.db = MySQLdb.connect('localhost', 'root', '123456', 'user_info')
 self.cursor = self.db.cursor()
 self.cursor.execute("select * from history order by time")
 self.Text.SetValue('')
 for data in self.cursor.fetchall(): #加载历史聊天记录
 self.Text.AppendText('%s said:\n%s\nwhen %s\n\n' % (data[0], data[2], data[1]))
 '''数据库准备工作,用于存储聊天记录'''


 #将聊天记录导出到EXCEl表中
 def WriteToExcel(self,event):
 wbk = xlwt.Workbook()
 sheet = wbk.add_sheet('sheet 1')
 self.cursor.execute("select * from history order by time")
 sheet.write(0, 0, "User")
 sheet.write(0, 1, "Datetime")
 sheet.write(0, 5, "Message")
 index = 0
 for data in self.cursor.fetchall():
 index = index + 1
 Time = '%s'%data[1] #将datetime转成字符形式,否则直接写入Excel会变成时间戳
 sheet.write(index,0,data[0])
 sheet.write(index,1,Time) #写进EXCEL会变成时间戳
 sheet.write(index,5,data[2])
 wbk.save(r'D:\History_Dialog.xls')


 #启动服务器的服务线程
 def Start(self,event):
 if not self.onServe:
 '''启动服务线程'''
 self.onServe = True
 mainThread = threading.Thread(target=self.on_serving, args=())
 mainThread.setDaemon(True) # 解决父线程结束,子线程还继续运行的问题
 mainThread.start()
 '''启动服务线程'''

 #关闭服务器
 def Break(self,event):
 self.onServe = False

 #服务器主循环
 def on_serving(self):
 print '...On serving...'
 while self.onServe:
 UserSocket, UserAddr = self.ServeSock.accept()
 username = UserSocket.recv(1024).decode(encoding='utf-8') #接收用户名
 userthread = ClientThread(UserSocket, username,self)
 self.UserThreadList.append(userthread) #将用户线程加到队列中
 userthread.start()
 self.ServeSock.close()

 #绑定发送按钮
 def SendMessage(self,event):
 if self.onServe and cmp(self.ttex.GetValue(),''):
 data = self.ttex.GetValue()
 self.AddText('Server',data,time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
 self.ttex.SetValue('')


 # 向所有客户端(包括自己)发送信息,同时更新到数据库
 def AddText(self, source, data,Time):
 self.cursor.execute("insert into history values(\"%s\",\"%s\",\"%s\")" % (source,Time,data)) #双引号里面有双引号,bug:句子不能有双引号、以及中文
 self.db.commit()
 sendData = '%s said:\n%s\nwhen %s\n' % (source,data,Time)
 self.Text.AppendText('%s\n'%sendData)
 for user in self.UserThreadList: #bug:客户端关闭了仍然在队列中。如果客户端关闭了,那怎么在服务器判断是否已经关闭了?客户端在关闭之前发一条信息给服务器?
 user.UserSocket.send(sendData.encode(encoding='utf-8'))

 #绑定清空按钮
 def EditClear(self,event):
 self.ttex.Clear()


def main():
 app = wx.App(False)
 Server().Show()
 app.MainLoop()

if __name__ == '__main__':
 main()

服务器的客户线程Clientthread:

# -*- coding: UTF-8 -*-

import threading
import time

class ClientThread(threading.Thread):

 def __init__(self,UserSocket, Username,server):
 threading.Thread.__init__(self)
 self.UserSocket = UserSocket
 self.Username = Username
 self.server = server
 self.Loadhist()

 # 加载历史聊天记录
 def Loadhist(self):
 self.server.cursor.execute("select * from history order by time")
 for data in self.server.cursor.fetchall():
 time.sleep(0.6)  #几条信息同时发,会造成末尾回车键的丢失,所以要有时间间隔
 sendData = '%s said:\n%s\nwhen %s\n'%(data[0], data[2], data[1])
 self.UserSocket.send(sendData.encode(encoding='utf-8'))


 #方法重写,线程的入口
 def run(self):
 size = 1024
 while True:
 data = self.UserSocket.recv(size) #未解决:客户端断开连接后这里会报错
 self.server.AddText(self.Username,data.decode(encoding='utf-8'),time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
 self.UserSocket.close() #这里都执行不到

客户登录界面Logframe:

# -*- coding: UTF-8 -*-

from socket import *
import wx
import MySQLdb
from client import Client

class LogFrame(wx.Frame):
 def __init__(self,parent=None,id=-1,title='登录窗口',pos=wx.DefaultPosition,size=(500,300)):

 '''窗口'''
 wx.Frame.__init__(self,parent,id,title,pos,size=(400,280))
 self.pl = wx.Panel(self)
 con = wx.BoxSizer(wx.VERTICAL)
 subcon = wx.FlexGridSizer(2,2,10,10)
 username = wx.StaticText(self.pl, label="Username:",style=wx.ALIGN_LEFT)
 password = wx.StaticText(self.pl, label="Password:",style=wx.ALIGN_LEFT)
 self.tc1 = wx.TextCtrl(self.pl,size=(180,20))
 self.tc2 = wx.TextCtrl(self.pl,size=(180,20),style=wx.TE_PASSWORD)
 subcon.Add(username,wx.TE_LEFT)
 subcon.Add(self.tc1,1,wx.EXPAND)
 subcon.Add(password)
 subcon.Add(self.tc2,1,wx.EXPAND)
 con.Add(subcon,1,wx.ALIGN_CENTER)
 subcon2 = wx.FlexGridSizer(1,2,10,10)
 register = wx.Button(self.pl,label='Register')
 login = wx.Button(self.pl,label='Login')
 subcon2.Add(register,1, wx.TOP)
 subcon2.Add(login,1, wx.TOP)
 con.Add(subcon2,1,wx.ALIGN_CENTRE)
 self.pl.SetSizer(con)
 self.Bind(wx.EVT_BUTTON,self.Register,register)
 self.Bind(wx.EVT_BUTTON,self.Login,login)
 '''窗口'''
 self.isConnected = False
 self.userSocket = None

 #连接到服务器
 def ConnectToServer(self):
 if not self.isConnected:
 ADDR = ('localhost', 21567)
 self.userSocket = socket(AF_INET, SOCK_STREAM)
 try:
 self.userSocket.connect(ADDR)
 self.userSocket.send(self.tc1.GetValue().encode(encoding='utf-8'))
 self.isConnected = True
 return True
 except Exception:
 return False
 else:
 return True

 #登录
 def Login(self,event):
 if not self.ConnectToServer():
 err = wx.MessageDialog(None, '服务器未启动', 'ERROR!', wx.OK)
 err.ShowModal()
 err.Destroy()
 else:
 username = self.tc1.GetValue()
 password = self.tc2.GetValue()
 db = MySQLdb.connect('localhost', 'root', '123456', 'user_info')
 cursor = db.cursor()
 cursor.execute("select * from user_list where username='%s' and password='%s'"%(username,password))
 if not cursor.fetchone():
 err = wx.MessageDialog(None,'用户不存在或密码错误','ERROR!',wx.OK)
 err.ShowModal()
 else:
 self.Close()
 Client(opSock=self.userSocket, username=username).Show()
 db.commit()
 db.close()

 #注册
 def Register(self,event):
 if not self.ConnectToServer():
 err = wx.MessageDialog(None, '服务器未启动', 'ERROR!', wx.OK)
 err.ShowModal()
 err.Destroy()
 else:
 username = self.tc1.GetValue()
 password = self.tc2.GetValue()
 db = MySQLdb.connect('localhost', 'root', '123456', 'user_info')
 cursor = db.cursor()
 cursor.execute("select * from user_list where username='%s'"%username)
 if not cursor.fetchone():
 cursor.execute("insert into user_list(username,password) values('%s','%s')"%(username,password))
 else:
 err = wx.MessageDialog(None, '用户已存在', 'ERROR!', wx.OK)
 err.ShowModal()
 db.commit()
 db.close()


def main():
 app = wx.App(False)
 LogFrame().Show()
 app.MainLoop()

if __name__ == '__main__':
 main()

客户端Client:

#/usr/bin/env python
# -*- coding: UTF-8 -*-

import wx
import threading
from time import ctime

class Client(wx.Frame):
 def __init__(self,opSock,username,parent=None,id=-1,title='客户端',pos=wx.DefaultPosition,size=(500,300)):

 '''窗口'''
 wx.Frame.__init__(self,parent,id,title,pos,size=(400,470))
 self.opSock = opSock
 self.username = username
 pl = wx.Panel(self)
 con = wx.BoxSizer(wx.VERTICAL)
 subcon = wx.FlexGridSizer(wx.HORIZONTAL)
 sta = wx.Button(pl, size=(200, 40),label='连接')
 end = wx.Button(pl, size=(200, 40),label='断开')
 subcon.Add(sta, 1, wx.TOP|wx.LEFT)
 subcon.Add(end, 1, wx.TOP|wx.RIGHT)
 con.Add(subcon,1,wx.ALIGN_CENTRE)
 self.Text = wx.TextCtrl(pl, size=(400,250),style = wx.TE_MULTILINE|wx.TE_READONLY)
 con.Add(self.Text, 1, wx.ALIGN_CENTRE)
 self.ttex = wx.TextCtrl(pl, size=(400,100),style=wx.TE_MULTILINE)
 con.Add(self.ttex, 1, wx.ALIGN_CENTRE)
 sub2 = wx.FlexGridSizer(wx.HORIZONTAL)
 clear = wx.Button(pl, size=(200, 40), label='清空')
 send = wx.Button(pl, size=(200, 40), label='发送')
 sub2.Add(clear, 1, wx.TOP | wx.LEFT)
 sub2.Add(send, 1, wx.TOP | wx.RIGHT)
 con.Add(sub2, 1, wx.ALIGN_CENTRE)
 pl.SetSizer(con)
 '''窗口'''

 '''绑定'''
 self.Bind(wx.EVT_BUTTON, self.EditClear, clear)
 self.Bind(wx.EVT_BUTTON, self.Send, send)
 self.Bind(wx.EVT_BUTTON, self.Login, sta)
 self.Bind(wx.EVT_BUTTON, self.Logout, end)
 '''绑定'''
 self.isConnected = False

 #登录
 def Login(self,event):
 '''客户端准备工作'''
 self.isConnected = True
 t = threading.Thread(target=self.Receive, args=())
 t.setDaemon(True)
 t.start()
 '''客户端准备工作'''

 #退出
 def Logout(self,event):
 self.isConnected = False

 #绑定发送按钮
 def Send(self,event):
 if self.isConnected and cmp(self.ttex.GetValue(),''):
 self.opSock.send(self.ttex.GetValue().encode(encoding='utf-8'))
 self.ttex.SetValue('')

 #绑定清空按钮
 def EditClear(self,event):
 self.ttex.Clear()

 #接收客户端的信息(独立一个线程)
 def Receive(self):
 while self.isConnected:
 data = self.opSock.recv(1024).decode(encoding='utf-8')
 self.Text.AppendText('%s\n'%data)

更多关于python聊天功能的精彩文章请点击专题: python聊天功能汇总

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python实现统计英文单词个数及字符串分割代码
May 28 Python
python简单猜数游戏实例
Jul 09 Python
CentOS下使用yum安装python-pip失败的完美解决方法
Aug 16 Python
详解K-means算法在Python中的实现
Dec 05 Python
python实现扫描局域网指定网段ip的方法
Apr 16 Python
Python3 使用pillow库生成随机验证码
Aug 26 Python
python元组和字典的内建函数实例详解
Oct 22 Python
Python网络爬虫四大选择器用法原理总结
Jun 01 Python
使用Dajngo 通过代码添加xadmin用户和权限(组)
Jul 03 Python
Python如何读写二进制数组数据
Aug 01 Python
Pycharm快捷键配置详细整理
Oct 13 Python
Python爬虫实现selenium处理iframe作用域问题
Jan 27 Python
Python实现将数据写入netCDF4中的方法示例
Aug 30 #Python
Python使用爬虫抓取美女图片并保存到本地的方法【测试可用】
Aug 30 #Python
Python使用一行代码获取上个月是几月
Aug 30 #Python
Python实现的读取/更改/写入xml文件操作示例
Aug 30 #Python
python实现录音小程序
Oct 26 #Python
Python图像处理之简单画板实现方法示例
Aug 30 #Python
浅析python中numpy包中的argsort函数的使用
Aug 30 #Python
You might like
php中的动态调用实例分析
2015/01/07 PHP
combox改进版 页面原型参考dojo的,比网上jQuery的那些combox功能强,代码更小
2010/04/15 Javascript
基于jquery的划词搜索实现(备忘)
2010/09/14 Javascript
JS取request值以及自动执行使用示例
2014/02/24 Javascript
JavaScript控制table某列不显示的方法
2015/03/16 Javascript
jquery插件bxslider用法实例分析
2015/04/16 Javascript
jQuery实现类似淘宝网图片放大效果的方法
2015/07/08 Javascript
Jquery ajax 同步阻塞引起的UI线程阻塞问题
2015/11/17 Javascript
Bootstrap4一次重大更新 几乎涉及每行代码
2016/05/16 Javascript
bootstrap模态框垂直居中效果
2016/12/03 Javascript
Angular的事件和表单详解
2016/12/26 Javascript
Webpack 服务器端代码打包的示例代码
2017/09/19 Javascript
vue实现同一个页面可以有多个router-view的方法
2018/09/20 Javascript
详解vue如何使用rules对表单字段进行校验
2018/10/17 Javascript
Jquery异步上传文件代码实例
2019/11/13 jQuery
element-ui封装一个Table模板组件的示例
2021/01/04 Javascript
Python合并两个字典的常用方法与效率比较
2015/06/17 Python
Python 多进程和数据传递的理解
2017/10/09 Python
python脚本开机自启的实现方法
2019/06/28 Python
Python 网络编程之TCP客户端/服务端功能示例【基于socket套接字】
2019/10/12 Python
Ubuntu下Python+Flask分分钟搭建自己的服务器教程
2019/11/19 Python
python实现简单坦克大战
2020/03/27 Python
Python3合并两个有序数组代码实例
2020/08/11 Python
PyCharm 解决找不到新打开项目的窗口问题
2021/01/15 Python
新加坡第一大健康与美容零售商:屈臣氏新加坡(Watsons Singapore)
2020/12/11 全球购物
J2EE面试题
2016/03/14 面试题
大学生毕业自我鉴定范文
2013/09/19 职场文书
宿舍使用违章电器检讨书
2014/01/12 职场文书
课外活动总结范文
2014/07/09 职场文书
四风问题查摆材料
2014/08/25 职场文书
自我检讨书范文
2015/01/28 职场文书
以权谋私检举信范文
2015/03/02 职场文书
党支部半年考察意见
2015/06/01 职场文书
微信早安问候语
2015/11/10 职场文书
Python中常见的导入方式总结
2021/05/06 Python
5个pandas调用函数的方法让数据处理更加灵活自如
2022/04/24 Python