• 【Python网络编程】多线程聊天软件程序


    课程设计的时候制作的多线程聊天软件程序

    基于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()
  • 相关阅读:
    字符串去特定字符
    字符串的查找删除
    输出梯形
    元素节点的 innerText、innerHTML、outerHTML、outerText
    JavaScript DOM 特殊集合Collection
    Collection 访问方式
    JS Browser BOM
    异常
    JCBD
    try-with-resources 方式关闭注意事项
  • 原文地址:https://www.cnblogs.com/Arago/p/5162618.html
Copyright © 2020-2023  润新知