最近应课程要求,设计一个基于Python的网络程序,所实现的功能为:聊天、发送文件。
服务器运行截图:
客户端运行截图:
客户端代码:
# -*- coding: utf-8 -*-
from tkinter import *
import socket
import threading
import time
from tkinter.filedialog import askopenfilename
#===========================================================================
# 子界面类
class Window:
def __init__(self,sub_page,resulttext):
self.resulttext = resulttext
self.sub_page=sub_page
self.sub_page.title("正在聊天")
self.sub_page.geometry('940x700')
self.sub_page.resizable(width=False, height=False) # 宽不可变, 高可变,默认为True
self.label_text = Label(sub_page,text="聊天记录", font=('楷体', 14)).place(x=0, y=10)
self.text = Text(sub_page , height=20, width=59, font=("楷体", 18))
self.text.place(x=0, y=35)
self.clean_Button = Button(sub_page,text ="清除聊天记录",default='active',command =self.qingchu,width=10,height=1).place(x=630,y=510) #定义按钮按下去就调用函数
self.label_send = Label(sub_page,text="发送消息处",font=('楷体', 14)).place(x=0, y=520)
self.entrySend = Text(sub_page, height=5, width=51,font=('楷体', 20))
self.entrySend.place(x=0, y=550)
self.send_Button = Button(sub_page,text ="发送",default='active',command = self.send_mail,width=8,height=1).place(x=650,y=660) #定义按钮按下去就调用函数
self.shutdown_Button = Button(sub_page,text ="退出",default='active',command = self.shout_down,width=8,height=1).place(x=570,y=660) #定义按钮按下去就调用函数
self.label_log = Label(sub_page,text="发送本地文件处",font=('宋体', 20)).place(x=730, y=0)
self.label_add = Label(sub_page,text="本地文件地址:",font=('宋体', 16)).place(x=750, y=50)
self.file_add = Text(sub_page , height=2, width=30, font=("楷体",10))
self.file_add.place(x=720, y=80)
self.send_choose = Button(sub_page,text ="本地文件选择",default='active',command = self.choicefile,width=12,height=1).place(x=780,y=120) #定义按钮按下去就调用函数
self.send_Document = Button(sub_page,text ="上传",default='active',command = self.shangchuan,width=6,height=2).place(x=800,y=160) #定义按钮按下去就调用函数
self.photo_1 = PhotoImage(file="QQxiu.png")
self.image2_label = Label(sub_page,image= self.photo_1).place(x=710,y=220)
def listen(self):
self.ctr=control(self.edit)
self.ctr.setDaemon(True)
self.ctr.start()
def close(self):
self.ctr.stop()
def qingchu(self): #清除聊天记录
self.text.delete(1.0,END)
def send_mail(self):
self.resulttext = self.entrySend.get(0.0,END)
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'我:' + self.resulttext + '\n')
self.entrySend.delete('0.0', END)
tcp_socket.send(("0"+self.resulttext).encode()) #前面+0是为了说明发送的是文本而非文件
def shout_down(self): # 销毁页面
sub_page.withdraw()
root.update()
root.deiconify()
def shangchuan(self):
self.filename = self.path.split("/")[-1]
with open(self.path, 'r',encoding='UTF-8') as f:
tcp_socket.send(("1"+self.filename+"$$"+f.read()).encode()) #前面+1是为了说明发送的是文件而非文本
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n' + "发送文件:"+self.filename+ '\n')
def choicefile(self):
self.path = askopenfilename()
self.file_add.insert(1.0,self.path)
def jieshou(self):
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'他:' + self.resulttext + '\n')
#==========================================================================
def jieshou():#接受服务端发送数据
while True:
file_name = tcp_socket.recv(4096)
rev = file_name.decode()
print("接受到:")
print(rev)
if rev[0]=="0":
window.resulttext = rev[1:]#此处修改实例变量的值,resulttext为聊天记录框的字符
window.jieshou()#显示接收的消息
elif rev[0]=="1":
name =rev[1:].split("$$")[0]
with open(name,'w')as file:
file.write(rev[len(name)+3:])
file.close()
window.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n' + "接收到文件:"+name+ '\n')
def lianjie(): #登录连接
IP = ipentry.get()
PORT = int(portentry.get())
ADDR = (IP,PORT)
try:
tcp_socket.connect(ADDR)
sub_page.deiconify()
root.withdraw()
t1 = threading.Thread(target=jieshou )
t1.start()
except Exception as error:
print(error) #打印网络连接错误类型
#=====================================================================================================
#主界面
root = Tk() #设定底板
root.geometry("320x300")
root.title("用户端APP")
root.resizable(width=False, height=False) # 宽不可变, 高可变,默认为True
ip = StringVar()
ip.set("127.0.0.1")
lianjie_state = StringVar()
lianjie_state.set("请连接网络")
port = IntVar()
port.set("8888")
global fileurl1
fileurl1 = StringVar()
global resulttext
resulttext = str()
photo = PhotoImage(file="QQ1.png")
image_label = Label(root,compound='center',image= photo).place(x=120,y=10)
iplabel = Label(root, text="输入ip:",font="Helvetica -15 bold").place(x=60,y=130)
ipentry = Entry(root,textvariable=ip,width=15)
ipentry.place(x=120,y=130)
portlabel = Label(root,text="输入端口:",font="Helvetica -15 bold").place(x=40,y=160) #文字标签字体
portentry = Entry(root, textvariable=port,width=15) #输入框内容和值 宽度
portentry.place(x=120,y=160)
ok = Button(root,text ="连接",default='active',command = lianjie,width=8,height=2).place(x=60,y=200) #定义按钮按下去就调用函数
quitscan= Button(root,text="退出", default='active',command=quit, width=8,height=2,bg = 'red').place(x=200,y=200)
####################################################################################################################################
# 用窗口类创建子页面
sub_page=Toplevel()
sub_page.withdraw()
window = Window(sub_page,resulttext) # 入口参数为(页面底板,以及传送套接字符)
global tcp_socket
tcp_socket =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 页面循环
sub_page.mainloop()
root.mainloop()
####################################################################################################################################
服务器代码:
# -*- coding: utf-8 -*-
from tkinter import *
from socketserver import BaseRequestHandler,ThreadingTCPServer
import threading
import time
import socket
from tkinter.scrolledtext import ScrolledText
from tkinter.filedialog import askopenfilename
def start():
page.deiconify()
root.withdraw()
def connect():
IP = ipentry.get()
PORT = int(portentry.get())
ADDR = (IP,PORT)
tcp_socket.bind(ADDR)
tcp_socket.listen(10)
global client_socket
client_socket,client_addr = tcp_socket.accept()
while True:
# 利用accept获取分套接字以及客户端的地址
# 接收客户端的数据
file_name = client_socket.recv(4096)
rev=file_name.decode()
if rev[0]=="0":
window.resulttext = rev[1:]#此处修改实例变量的值,resulttext为聊天记录框的字符
window.jieshou()#显示接收的消息
elif rev[0]=="1":
name =rev[1:].split("$$")[0]
with open(name,'w')as file:
file.write(rev[len(name)+3:])
file.close()
window.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n' + "接收到文件:"+name+ '\n')
#===========================================================================
# 子界面类
class Window:
def __init__(self,sub_page,resulttext):
self.resulttext = resulttext
self.sub_page=sub_page
self.sub_page.title("正在聊天")
self.sub_page.geometry('940x700')
self.sub_page.resizable(width=False, height=False) # 宽不可变, 高可变,默认为True
self.label_text = Label(sub_page,text="聊天记录", font=('楷体', 14)).place(x=0, y=10)
self.text = Text(sub_page , height=20, width=59, font=("楷体", 18))
self.text.place(x=0, y=35)
self.clean_Button = Button(sub_page,text ="清除聊天记录",default='active',command =self.qingchu,width=10,height=1).place(x=630,y=510) #定义按钮按下去就调清除聊天记录用函数
self.label_send = Label(sub_page,text="发送消息处",font=('楷体', 14)).place(x=0, y=520)
self.entrySend = Text(sub_page, height=5, width=51,font=('楷体', 20))
self.entrySend.place(x=0, y=550)
self.send_Button = Button(sub_page,text ="发送",default='active',command = self.send_mail,width=8,height=1).place(x=650,y=660) #定义按钮按下去就调用发送函数
self.shutdown_Button = Button(sub_page,text ="退出",default='active',command = self.shout_down,width=8,height=1).place(x=570,y=660) #定义按钮按下去就调用退出函数
self.label_log = Label(sub_page,text="发送本地文件处",font=('宋体', 20)).place(x=730, y=0)
self.label_add = Label(sub_page,text="本地文件地址:",font=('宋体', 16)).place(x=750, y=50)
self.file_add = Text(sub_page , height=2, width=30, font=("楷体",10))
self.file_add.place(x=720, y=80)
self.send_choose = Button(sub_page,text ="本地文件选择",default='active',command = self.choicefile,width=12,height=1).place(x=780,y=120) #定义按钮按下去就调本地文件选择用函数
self.send_Document = Button(sub_page,text ="上传",default='active',command = self.shangchuan,width=6,height=2).place(x=800,y=160) #定义按钮按下去就调用上传文件函数
self.photo_1 = PhotoImage(file="QQxiu1.png") #加载QQ秀图片
self.image2_label = Label(sub_page,image= self.photo_1).place(x=710,y=220)
def listen(self):
self.ctr=control(self.edit)
self.ctr.setDaemon(True)
self.ctr.start()
def close(self):
self.ctr.stop()
def qingchu(self): #清除聊天记录
self.text.delete(1.0,END)
def send_mail(self):
self.resulttext = self.entrySend.get(0.0,END)
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'我:' + self.resulttext + '\n')
self.entrySend.delete('0.0', END)
client_socket.send(("0"+self.resulttext).encode()) #前面+0是为了说明发送的是文本而非文件
def shout_down(self): # 退出函数,作用销毁子聊天页面
self.sub_page.destroy()
def shangchuan(self):
self.filename = self.path.split("/")[-1]
with open(self.path, 'r',encoding="utf-8") as f:
client_socket.send(("1"+self.filename+"$$"+f.read()).encode()) #前面+1是为了说明发送的是文件而非文本
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n' + "发送文件:"+self.filename+ '\n')
def choicefile(self):
self.path = askopenfilename()
self.file_add.insert(1.0,self.path)
def jieshou(self):
self.text.insert(END,time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) + '\n'+'他:' + self.resulttext + '\n')
#==========================================================================
# 主界面
root = Tk() #设定底板
root.geometry("600x540")
root.title("服务器端")
root.resizable(width=False, height=False)
global ip
ip = StringVar()
ip.set("127.0.0.1")
global port
port = IntVar()
port.set("8888")
global cmd
cmd = StringVar()
global fileurl1
fileurl1 = StringVar()
global resulttext
resulttext = str()
title_label = Label(root,text="服务器端",bg = 'yellow',font="宋体 -30 bold").place(x=240,y=20)
iplabel = Label(root,text="服务器ip:",font="宋体 -20 bold").place(x=30,y=70)
ipentry = Entry(root,textvariable=ip,width=40,state="readonly")
ipentry.place(x=140,y=70)
portlabel = Label(root,text="服务器端口:",font="宋体 -20 bold").place(x=10,y=100)
portentry = Entry(root,textvariable=port,width=40,state="readonly")
portentry.place(x=140,y=100)
photo = PhotoImage(file="QQ.jpg")
image_label = Label(root,compound='center',image= photo)
image_label.place(x=137,y=125)
warnlabel = Label(root,text="请合理安排上网时长,避免沉迷于网络",font="宋体 -20 bold").place(x=120,y=500)
startscan = Button(root,text="登录", default='active',command=start, width=8,height=1).place(x=20,y=500)
quitscan = Button(root,text="退出", default='active',command=quit, width=8,height=1).place(x=520,y=500)
#===================================================================================
#子页面
global tcp_socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创造窗口类对象
page = Toplevel()
page.withdraw()
window = Window(page,resulttext)
# 开启网络接收新线程
t1 = threading.Thread(target=connect)
t1.start()
# 页面循环
page.mainloop()
root.mainloop()
###############################################################################################################
运行还需要几张图片,所以我贴上完整的百度云链接:https://pan.baidu.com/s/1p5nrMgW24RoYd7PdMjyb8Q
提取码:52dk