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引用DLL文件的方法
May 11 Python
Django中URL视图函数的一些高级概念介绍
Jul 20 Python
利用numpy和pandas处理csv文件中的时间方法
Apr 19 Python
python如何以表格形式打印输出的方法示例
Jun 21 Python
pyqt5移动鼠标显示坐标的方法
Jun 21 Python
Python+opencv 实现图片文字的分割的方法示例
Jul 04 Python
python3实现带多张图片、附件的邮件发送
Aug 10 Python
在OpenCV里使用特征匹配和单映射变换的代码详解
Oct 23 Python
Python 解析库json及jsonpath pickle的实现
Aug 17 Python
Pycharm同步远程服务器调试的方法步骤
Nov 04 Python
Python爬虫教程之利用正则表达式匹配网页内容
Dec 08 Python
selenium携带cookies模拟登陆CSDN的实现
Jan 19 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数据库密码的找回的步骤
2011/01/12 PHP
基于curl数据采集之正则处理函数get_matches的使用
2013/04/28 PHP
Laravel 5.0 发布 新版本特性详解
2015/02/10 PHP
php生成短域名函数
2015/03/23 PHP
WordPress中获取指定分类及其子分类下的文章数目
2015/12/31 PHP
JavaScript/Js脚本处理html元素的自定义属性解析(亲测兼容Firefox与IE)
2013/11/25 Javascript
jquery清空表单数据示例分享
2014/02/13 Javascript
JS验证邮件地址格式方法小结
2015/12/01 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
基于JS实现bookstore静态页面的实例代码
2017/02/22 Javascript
node.js实现的装饰者模式示例
2017/09/06 Javascript
vue中SPA单页面应用程序详解
2017/11/07 Javascript
vue使用laydate时间插件的方法
2018/11/14 Javascript
基于vue通用表单解决方案的思考与分析
2019/03/16 Javascript
微信小程序实现侧边分类栏
2019/10/21 Javascript
JavaScript中变量提升和函数提升的详解
2020/08/07 Javascript
vue实现按钮切换图片
2021/01/20 Vue.js
[02:46]解说DC:感谢430陪伴我们的DOTA2国际邀请赛岁月
2016/06/29 DOTA
在Python中使用cookielib和urllib2配合PyQuery抓取网页信息
2015/04/25 Python
Python SQLite3数据库日期与时间常见函数用法分析
2017/08/14 Python
tf.truncated_normal与tf.random_normal的详细用法
2018/03/05 Python
python opencv判断图像是否为空的实例
2019/01/26 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
Python异常原理及异常捕捉实现过程解析
2020/03/25 Python
python读取图像矩阵文件并转换为向量实例
2020/06/18 Python
Pycharm调试程序技巧小结
2020/08/08 Python
造价工程师个人求职信
2013/09/21 职场文书
减负增效提质方案
2014/05/23 职场文书
奉献家乡演讲稿
2014/09/13 职场文书
2014年群众路线党员自我评议
2014/09/24 职场文书
2014年乡镇安全生产工作总结
2014/12/02 职场文书
2015年前台个人工作总结
2015/04/03 职场文书
2015年医院药剂科工作总结
2015/05/04 职场文书
总结python多进程multiprocessing的相关知识
2021/06/29 Python
Python集合的基础操作
2021/11/01 Python
mysql数据库隔离级别详解
2022/06/16 MySQL