课程设计的时候制作的多线程聊天软件程序
基于python3.4.3
import socket import pickle import threading import tkinter import os import datetime import time try: import pymysql except: print("can‘t find pymysql") tcplocalport=8101 #TCP监听端口 tcpconnectport=8101 #TCP连接端口 udplocalport=8201 #广播监听端口 udpconnectport=8201 #广播连接端口 filelocalport=8301 #文件监听接口 fileconnectport=8301 #文件连接端口 localIP=socket.gethostbyname(socket.gethostname()) #本机IP print("localip:",localIP) print(‘localtcpport:‘,tcplocalport) #sockdict字典用于存放本机作为客户端的连接对象 sockdict={} #filedict字典用于存放文件传输的信息 #filedict[‘IP‘]=[‘filename‘,filesize,fpobj,restsize] filedict={} sendfileflag={} #用于调用软件的图形界面 showdict={} #转换用表 #a为address,i为listbox的index值,n为name a2i={} a2n={} i2a={} myname=‘a‘ sqlflag=0 #MYSQL启用标志,若启用MYSQL,则为1 #用于通讯的类,type为通讯指令的类型,name为发送端的名称 #data为正文,target为目标对象,time为发送时的时间 class talkdata: def __init__(self,ttype,tname,tdata,ttarget=""): self.dtype=ttype self.dname=tname self.ddata=tdata self.dtarget=ttarget self.dtime=gettime() #获取时间的函数 def gettime(): now = datetime.datetime.now() return now.strftime(‘%Y-%m-%d %H:%M:%S‘) #解析接收到的数据 def solvedata(data_str,address,protocols): global sockdict global filedict global a2n global sendfileflag data_obj=pickle.loads(data_str) if data_obj.dtype=="searchse" and (address[0]!=localIP or data_obj.dname!=myname): #搜索发起类型 reply=talkdata("searchre",myname,"") if sockdict.get(address[0])==None: socketconnect(address[0],tcpconnectport) send(sockdict[address[0]],reply) if data_obj.dtype=="searchre" and (address[0]!=localIP or data_obj.dname!=myname): #搜索回复类型 a2n[address[0]]=data_obj.dname """if data_obj.dtype=="tcpcon": if showdict.get(address[0])==None: socketconnect(address[0],tcpconnectport) threading.Thread(target= createfgui_guif,arg= (sockdict[address[0]],(address[0],tcpconnectport))).start() """ if data_obj.dtype=="str": #聊天内容类型 adata=data_obj.dtime+"\n"+data_obj.dname+‘:‘+data_obj.ddata if showdict.get(address[0])==None: a2n[address[0]]=data_obj.dname socketconnect(address[0],tcpconnectport) threading.Thread(target= createfgui_guif,args= (sockdict[address[0]],(address[0],tcpconnectport))).start() time.sleep(0.5) showdict[address[0]].insert(‘end‘,adata) print(data_obj.ddata) if data_obj.dtype=="filehead": #发送文件头类型 filename=data_obj.ddata[0] filesize=data_obj.ddata[1] restsize=filesize print("filename:",filename) print("filesize:",filesize) filetk=tkinter.Tk() filetk.withdraw() fd=tkinter.filedialog.FileDialog(filetk) filesavename=fd.go() filetk.destroy() if filesavename==None: filef=talkdata(‘fileflag‘,myname,"refuse") else: #fp=open(filename,‘wb‘) #fp=open(input("filename:"),‘wb‘) fp=open(filesavename,‘wb‘) filedict[address[0]]=[filename,filesize,fp,restsize] if sockdict.get(address[0])==None: socketconnect(address[0],tcpconnectport) filef=talkdata(‘fileflag‘,myname,"accept") send(sockdict[address[0]],filef) if data_obj.dtype=="fileflag": #文件允许接收类型 if data_obj.ddata=="accept": sendfileflag[address[0]]=1 print("start send file") if data_obj.ddata=="refuse": sendfileflag[address[0]]=2 if data_obj.dtype=="udpstr" and (address[0]!=localIP or data_obj.dname!=myname): #广播聊天内容类型 adata=data_obj.dtime+"\n"+data_obj.dname+‘:‘+data_obj.ddata showdict[data_obj.dtarget].insert(‘end‘,adata) if sqlflag==1 and protocols==‘tcp‘: sqlsavedata(data_obj,address[0]) #连接MYSQL def sqlinit(): global sqlcon,sqlcur,sqlflag sqlcon=pymysql.connect(host=‘localhost‘,user=‘root‘,passwd=‘‘,port=3306) sqlcur=sqlcon.cursor() try : sqlcon.select_db(‘talk‘) except : sqlcur.execute(‘create database if not exists talk‘) sqlcon.select_db(‘talk‘) sqlcon.commit() sqlflag=1 print(‘connect sql‘) #创建表 def sqlcreattable(ip): global sqlcon,sqlcur daip=‘‘ for i in ip: if i==‘.‘: i=‘_‘ daip=daip+i sqlcur.execute("""create table if not exists %s (type varchar(20), name varchar(20), data varchar(500), time datetime)""" % daip) sqlcon.commit() #将数据加入数据库 def sqlsavedata(data_obj,ip): global sqlcon,sqlcur daip=‘‘ for i in ip: if i==‘.‘: i=‘_‘ daip=daip+i value="insert into %s values(‘%s‘,‘%s‘,‘%s‘,‘%s‘)" % (daip,data_obj.dtype,data_obj.dname,data_obj.ddata,data_obj.dtime) #print(value) sqlcur.execute(value) sqlcon.commit() #读取数据库中的数据 def sqlloaddata(ip): global sqlcon,sqlcur daip=‘‘ for i in ip: if i==‘.‘: i=‘_‘ daip=daip+i sqlcur.execute(‘select * from %s‘ % daip) a=sqlcur.fetchall() sqlcon.commit() return a #发送聊天 def send(socketobj,data_obj): data_pickle=pickle.dumps(data_obj) try : socketobj.send(data_pickle) except ConnectionResetError : address=socketobj.getpeername() socketconnect(address[0],address[1]) sockdict[address[0]].send(data_pickle) #广播聊天对象 def sendudp(data_obj): data_str=pickle.dumps(data_obj) addr=(‘<broadcast>‘,udpconnectport) udps=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udps.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1) udps.sendto(data_str,addr) #发送文件 def sendfile(socketobj,filename): fp=open(filename,‘rb‘) filesize=os.stat(filename).st_size fhead=talkdata("filehead",myname,(filename,filesize)) address=socketobj.getpeername() send(socketobj,fhead) sendfileflag[address[0]]=0 while sendfileflag[address[0]]==0: pass if sendfileflag[address[0]]==2: return 0 socketfile=socket.socket(socket.AF_INET,socket.SOCK_STREAM) socketfile.connect((address[0],fileconnectport)) while 1: filedata=fp.read(1024) if not filedata: break socketfile.send(filedata) print("send over") #接收TCP信息 def rec(connect,address): while 1: #try: data_str=connect.recv(1024) if not data_str: print("leave") break print(‘Receive TCP data from‘,address) solvedata(data_str,address,‘tcp‘) #except: # print(‘Receive TCP data error\n‘) # break #接收文件 def recfile(connect,address): while 1: #try: filedata=connect.recv(1024) if not filedata: print("leave") break restsize=filedict[address[0]][3] print("Receive file data from",address,"\nRestsize:",restsize) fp=filedict[address[0]][2] fp.write(filedata) restsize=restsize-len(filedata) filedict[address[0]][3]=restsize if restsize<=0: fp.close() print("Receive file successful") #except: # print(‘Receive file data error\n‘) # break #接收广播信息 def socketudplisten(sockobj): while 1: data_str,address=sockobj.recvfrom(1024) if not data_str: pring("no udpdata") if sqlflag==1 : sqlcreattable(address[0]) solvedata(data_str,address,‘udp‘) #文件接收端口监听 def filelisten(sockobj): threfdict={} while 1: connect,address=sockobj.accept() print("File accept ",address) threfdict[address[0]]=threading.Thread(target=recfile,args=(connect,address)) threfdict[address[0]].start() #TCP端口监听 def socketlisten(sockobj): global sqlflag thredict={} while 1: connect,address=sockobj.accept() print("Accept ",address) if sqlflag==1 : sqlcreattable(address[0]) thredict[address[0]]=threading.Thread(target=rec,args=(connect,address)) thredict[address[0]].start() #连接TCP def socketconnect(connectIP,connectport): global sockdict try: sockse=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sockse.connect((connectIP,connectport)) print("connect") sockdict[connectIP]=sockse if sqlflag==1 : sqlcreattable(connectIP) except: print("tcp connect error") #登录按钮执行的功能 def login_guif(): global myname if entryname.get()!=‘‘: myname=entryname.get() listgui.title(myname) listgui.deiconify() logingui.withdraw() #ip连接对话框的连接按钮执行的功能 def getip_guif(): global i2a,a2i,a2n #连接对话框 tcpcongui=tkinter.Toplevel() framefip=tkinter.Frame(tcpcongui) framefip.pack() labelip=tkinter.Label(tcpcongui,text=‘IP地址‘) labelip.pack() entryip=tkinter.Entry(tcpcongui) entryip.pack() tkinter.Button(tcpcongui,text="连接",command=lambda :inputip_guif(tcpcongui,entryip)).pack() select=lbfriends.curselection() a=() if select==a: connectip="" else: connectip=i2a[select[0]] entryip.insert(0,connectip) #发送文件按钮执行的功能 def sendfile_guif(socketobj,tkobj): fd=tkinter.filedialog.FileDialog(tkobj) filename=fd.go() threading.Thread(target=sendfile,args=(socketobj,filename)).start() #创建私聊聊天窗口 def createfgui_guif(socketobj,address): global showdict global a2n if a2n.get(address[0])==None: name=address[0] else: name=a2n[address[0]] tl=tkinter.Tk() tl.title(name) framereceive=tkinter.Frame(tl) framereceive.pack() scrollbarreceive=tkinter.Scrollbar(framereceive) scrollbarreceive.pack(fill=‘y‘,side=‘right‘) showtext=tkinter.Text(framereceive,height=20, width=60,yscrollcommand=scrollbarreceive.set) showtext.pack(side=‘left‘) framesend=tkinter.Frame(tl) framesend.pack() scrollbarsend=tkinter.Scrollbar(framesend) scrollbarsend.pack(fill=‘y‘,side=‘right‘) sendtext=tkinter.Text(framesend,height=4, width=60,yscrollcommand=scrollbarsend.set) sendtext.pack(side=‘left‘) framebutton=tkinter.Frame(tl) framebutton.pack() tkinter.Button(framebutton,text=‘发送信息‘,command=lambda:send_guif(socketobj,showtext,sendtext)).pack(side=‘right‘) tkinter.Button(framebutton,text=‘发送文件‘,command=lambda:sendfile_guif(socketobj,tl)).pack(side=‘left‘) tkinter.Button(framebutton,text=‘聊天记录‘,command=lambda:loaddata_guif(address[0])).pack(side=‘left‘) showdict[address[0]]=showtext tl.mainloop() #创建广播聊天窗口 def createggui_guif(target): global showdict tl=tkinter.Tk() tl.title(target) framereceive=tkinter.Frame(tl) framereceive.pack() scrollbarreceive=tkinter.Scrollbar(framereceive) scrollbarreceive.pack(fill=‘y‘,side=‘right‘) showtext=tkinter.Text(framereceive,height=20, width=60,yscrollcommand=scrollbarreceive.set) showtext.pack(side=‘left‘) framesend=tkinter.Frame(tl) framesend.pack() scrollbarsend=tkinter.Scrollbar(framesend) scrollbarsend.pack(fill=‘y‘,side=‘right‘) sendtext=tkinter.Text(framesend,height=4, width=60,yscrollcommand=scrollbarsend.set) sendtext.pack(side=‘left‘) framebutton=tkinter.Frame(tl) framebutton.pack() tkinter.Button(framebutton,text=‘发送信息‘,command=lambda:sendudp_guif(target,showtext,sendtext)).pack(side=‘right‘) showdict[target]=showtext tl.mainloop() #聊天记录按钮执行的功能 def loaddata_guif(ip): sqldata=sqlloaddata(ip) datagui=tkinter.Tk() scrollbarreceive=tkinter.Scrollbar(datagui) scrollbarreceive.pack(fill=‘y‘,side=‘right‘) showtext=tkinter.Text(datagui,height=20, width=60,yscrollcommand=scrollbarreceive.set) showtext.pack(side=‘left‘) for tadata in sqldata: adata="%s %s \n%s:%s" % (tadata[3],tadata[0],tadata[1],tadata[2]) showtext.insert(‘end‘,adata) #私聊窗口中发送信息按钮执行的功能 def send_guif(socketobj,showtext,sendtext): data=sendtext.get(‘0.0‘,‘end‘) adata=gettime()+‘\n‘+myname+‘:‘+data sendtext.delete(‘0.0‘,‘end‘) showtext.insert(‘end‘,adata) data_obj=talkdata("str",myname,data) if sqlflag==1: address=socketobj.getpeername() sqlsavedata(data_obj,address[0]) send(socketobj,data_obj) #群聊窗口中发送信息按钮执行的功能 def sendudp_guif(target,showtext,sendtext): data=sendtext.get(‘0.0‘,‘end‘) adata=gettime()+‘\n‘+myname+‘:‘+data sendtext.delete(‘0.0‘,‘end‘) showtext.insert(‘end‘,adata) data_obj=talkdata("udpstr",myname,data,target) sendudp(data_obj) #IP对话框中连接按钮执行的功能 def inputip_guif(gui,entryip): global sockdict gui.withdraw() connectip=entryip.get() if connectip=="": connectip="127.0.0.1" socketconnect(connectip,tcpconnectport) threading.Thread(target= createfgui_guif,args= (sockdict[connectip],(connectip,tcpconnectport))).start() #我的群组中连接按钮执行的功能,暂未加入 def getgroup_guif(): target=lbgroups.selection_get() threading.Thread(target= createggui_guif,args=(target,)).start() #我的好友中刷新按钮执行的功能 def refreshf_guif(): global a2n global a2i global i2a a2n.clear() a2i.clear() i2a.clear() lbfriends.delete(0,‘end‘) data=talkdata("searchse",myname,(localIP,tcplocalport)) sendudp(data) time.sleep(1) i=0 for key in a2n: lbfriends.insert(i,a2n[key]) a2i[key]=i i2a[i]=key i=i+1 #我的群组中刷新按钮执行的功能 def refreshg_guif(): pass #登录界面 logingui=tkinter.Tk() logingui.title(‘登录‘) framelogin=tkinter.Frame(logingui) framelogin.pack() labelname=tkinter.Label(framelogin,text=‘名称‘) labelname.pack(side=‘left‘) entryname=tkinter.Entry(framelogin) entryname.pack(side=‘right‘) frameloginbutton=tkinter.Frame(logingui) frameloginbutton.pack() tkinter.Button(frameloginbutton,text=" 登录 ",command=login_guif).pack(side=‘right‘) tkinter.Button(frameloginbutton,text="MYSQL",command=sqlinit).pack(side=‘left‘) #列表界面 #listgui=tkinter.Toplevel() listgui=tkinter.Tk() listgui.withdraw() framelistfriends=tkinter.Frame(listgui) framelistfriends.pack(side=‘left‘) lablefriends=tkinter.Label(framelistfriends,text=‘我的好友‘) lablefriends.pack(side=‘top‘) lbfriends=tkinter.Listbox(framelistfriends) lbfriends.pack(side=‘top‘) framlistfbutton=tkinter.Frame(framelistfriends) framlistfbutton.pack() tkinter.Button(framlistfbutton,text="刷新",command=refreshf_guif).pack(side=‘left‘) tkinter.Button(framlistfbutton,text="连接",command=getip_guif).pack(side=‘right‘) framelistgroups=tkinter.Frame(listgui) framelistgroups.pack(side=‘right‘) lablegroups=tkinter.Label(framelistgroups,text=‘我的群组‘) lablegroups.pack(side=‘top‘) lbgroups=tkinter.Listbox(framelistgroups) lbgroups.pack(side=‘top‘) lbgroups.insert(0,"广播") framlistgbutton=tkinter.Frame(framelistgroups) framlistgbutton.pack() tkinter.Button(framlistgbutton,text="刷新",command=refreshg_guif).pack(side=‘left‘) tkinter.Button(framlistgbutton,text="连接",command=getgroup_guif).pack(side=‘right‘) #TCP套接字初始化 sockre=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sockre.bind((‘‘,tcplocalport)) sockre.listen(30) print(‘TCP listen‘) #接收文件初始化 sockref=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sockref.bind((‘‘,filelocalport)) sockref.listen(30) #UDP套接字初始化 sockreudp=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sockreudp.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sockreudp.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1) sockreudp.bind((‘‘,udplocalport)) #创建线程监听端口 thredlisten=threading.Thread(target=socketlisten,args=(sockre,)) thredlisten.start() threfilelisten=threading.Thread(target=filelisten,args=(sockref,)) threfilelisten.start() threudplisten=threading.Thread(target=socketudplisten,args=(sockreudp,)) threudplisten.start()
时间: 2024-10-14 07:01:33