赞
踩
链接:https://pan.baidu.com/s/1kzxiLTkvdxGAMgF3SQzcaw?pwd=vb9h
提取码:vb9h
设置好服务端的配置代码

登录界面

使用过程

服务端主要用于为客户端提供聊天服务和FTP服务,所以我分别创建了两个类为ChatServer和FtpServer
# 用户所需输入信息分界线--------------------------------------
IP_server = '127.0.0.1'
PORT = 6666
FTP_PORT = 21
# 关于ftp的设置
username_server = 'mary' # ftp登录用的用户名
password_server = '123456'
ftp_catalogue = 'C:/my_soft/python/code/lesson_code/ftp_test' # ftp的目录
user_power = 'elradfmwMT' # 权限
# 用户所需输入信息分界线--------------------------------------
(1)TCP连接模块,主要代码就是与客户端建立起TCP通信
(2)当前在线用户模块,主要就对当前连接的客户端数目进行判断,然后返回一个当前在线用户数
(3)接收信息模块,主要代码是接收客户端发来的信息,然后存入一个队列中
(4)发送信息模块,主要代码是对客户端发来的信息进行分析,分析完后转入对于的函数去执行相关操作,比如接收到的是聊天信息就发到聊天用户的客户端中,接收到的是图片信息就发送指定字符串给聊天用户的客户端,然后对指定图片执行FTP上传下载操作。
(5)服务器启动检查模块,主要是令服务端与客户端进行连接,然后在控制台内打印连接状况的信息。
# ----------------------------------------文件传输服务器1-------------------------- class FtpServer(): # 新建一个用户组 authorizer = DummyAuthorizer() # 将用户名,密码,指定目录,权限 添加到里面 authorizer.add_user(username_server, password_server, ftp_catalogue, perm=user_power) # adfmw # 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试 authorizer.add_anonymous(ftp_catalogue) handler = FTPHandler handler.authorizer = authorizer def run(self): # 开启服务器 server = FTPServer((IP_server, FTP_PORT), self.handler) server.serve_forever() # ----------------------------------------文件传输服务器2--------------------------
# 登录按钮
def login(*args):
global IP, PORT, user, ftpUser, ftpPassword
IP, PORT = entryIP.get().split(':') # 获取IP和端口号
PORT = int(PORT) # 端口号需要为int类型
user = entryUser.get()
ftpUser = entryFtpUser.get()
ftpPassword = entryFtpPassword.get()
if not user:
tkinter.messagebox.showerror('错误', message='请输入用户名')
else:
root1.destroy() # 关闭登录窗口
(1)先进行FTP登录,方便后续把图片发送到服务器中
(2)打开本地文件资源管理器,找到图片的位置,修改图片的大小,因为聊天窗口无法加载过大的图片
(3)利用FTP把图片上传到服务器中
(4)给服务器发送一个指定的消息,表示这是一个发送图片命令
def send(*args):
# 没有添加的话发送信息时会提示没有聊天对象
users.append('------群聊模式-------')
print(chat)
if chat not in users:
tkinter.messagebox.showerror('Send error', message='There is nobody to talk to!')
return
if chat == user:
tkinter.messagebox.showerror('Send error', message='Cannot talk with yourself in private!')
return
mes = entry.get() + ':;' + user + ':;' + chat # 添加聊天对象标记
s.send(mes.encode())
a.set('') # 发送后清空文本框
def private(*args):
global chat
# 获取点击的索引然后得到内容(用户名)
indexs = listbox1.curselection()
index = indexs[0]
if index > 0:
chat = listbox1.get(index)
# 修改客户端名称
if chat == '------群聊模式-------':
root.title(user + ' 的群聊窗口')
return
# 下面这两行用于在标题头处显示目前是谁和谁在聊天
ti = user + ' 与 ' + chat + ' 的聊天窗口'
root.title(ti)
(1)在聊天窗口上扩大面积,插入输入框让用户填写发送邮件所需的对应信息、
(2)收集用户输入的信息,用对应的变量来保存
(3)对邮件的附件类型进行判断,以合适方式保存好插入邮件中
(4)登录邮箱服务器,发送邮件,核心代码如下
def send_mail(): global fileName_mail_img global fileName_mail_file global fileName_mail_text global fileName_mail_video # 然后给对应变量赋值即可 sender = entry_sender.get() # sender = input('请输入一个电子邮箱地址(163/QQ):') username, domain = sender.split('@') if domain == '163.com': host = 'smtp.163.com' elif domain == 'qq.com': host = 'smtp.qq.com' else: # print('当前代码只识别163和QQ邮箱,请检查邮箱地址或修改代码。') exit() port = 25 body = entry_mail_body.get("1.0", "end") # 这个get里面的参数是获得所有信息的 # 输入密码,无回显,需要在cmd或PowerShell运行程序 userpwd = entry_sender_passwd.get() # 要群发的电子邮件地址 recipients = entry_recipients.get() # 登录邮箱服务器 with SMTP(host, port) as server: server.starttls() server.login(username, userpwd) # 创建邮件 msg = MIMEMultipart() msg.set_charset('utf-8') # 回复地址与发信地址可以不同 # 但是大部分邮件系统在回复时会提示 msg['Reply-to'] = sender # 设置发信人、收信人和主题 msg.add_header('From', sender) msg.add_header('To', recipients) msg.add_header('Subject', entry_mail_topic.get()) msg['Date'] = formatdate() msg['Message-Id'] = make_msgid() # 设置邮件文字内容 msg.attach(MIMEText(body, 'plain', _charset='utf-8')) # ---------------------------------这里为添加附件功能------------------- # 首先是要打开一个窗口,然后获取到对应的文件名 # 添加图片 fn = fileName_mail_img print(fn) if fn : with open(fn, 'rb') as fp: attachment = MIMEImage(fp.read()) attachment.add_header('content-disposition', 'attachment', filename=fn.split('/')[-1]) msg.attach(attachment) # 添加文本文件 fn = fileName_mail_text print(fn) if fn: with open(fn, 'rb') as fp: attachment = MIMEBase('text', 'txt') attachment.set_payload(fp.read()) encode_base64(attachment) attachment.add_header('content-disposition', 'attachment', filename=fn.split('/')[-1]) msg.attach(attachment) # 添加可执行程序 fn = fileName_mail_file print(fn) if fn: with open(fn, 'rb') as fp: attachment = MIMEApplication(fp.read(), _encoder=encode_base64) attachment.add_header('content-disposition', 'attachment', filename=fn.split('/')[-1]) msg.attach(attachment) # 添加音乐文件 fn = fileName_mail_video print(fn) if fn: with open(fn, 'rb') as fp: attachment = MIMEAudio(fp.read(), 'plain', _encoder=encode_base64) attachment.add_header('content-disposition', 'attachment', filename=fn.split('/')[-1]) msg.attach(attachment) # 发送邮件 server.send_message(msg) # 每次发完清空附件 fileName_mail_file = '' fileName_mail_text = '' fileName_mail_video = '' fileName_mail_img = ''
(1)增大聊天窗口的界面,为用户提供进行FTP操作的按钮
(2)根据之前用户输入的信息进行FTP登录
(3)利用ftp.dir函数获取FTP服务器内的文件名称,将其存入数组中,然后提供翻页按钮,每次取出14条文件名称展示到聊天窗口中,核心代码为
def show_ftp(): global now_page # 将打印出来的数据截取出对应的文件名(这个有点巧妙) dir_res = [] ftp.dir('.', dir_res.append) # 对当前目录进行dir(),将结果放入列表 # print(dir_res) print(len(dir_res)) # 打印列表长度 # 由文件名判断名称是文件还是目录 for i in range(14): # 防止数组越界 if (now_page*14)+i <= len(dir_res)-1: name = dir_res[(now_page*14)+i].split(' ')[-1] # print(name) # 将名称插入列表 list2.insert(tkinter.END, name) # 通过.号判断是否为文件或文件夹,从而赋予不同的颜色(暂时未测试过文件夹) if '.' not in name: list2.itemconfig(tkinter.END, fg='orange') else: list2.itemconfig(tkinter.END, fg='blue')
(4)提供文件上传或文件夹上传服务,如果是文件就直接调用FTP的函数上传,如果是文件夹就先切换到该文件夹中,再遍历其中的文件并依次上传到FTP服务器中。上传文件夹的核心代码如下
def upload_ftpdir(): # 思路:先获取到对应文件名,然后以二进制形式上传,ftp的目录信息是在服务端进行设置的 # 获取文件夹名,并判断ftp目录里是否存在该文件夹 fileName_ftp = tkinter.filedialog.askdirectory() fileName_ftp_end = fileName_ftp.split('/')[-1] dir = [] ftp.dir('.', dir.append) # 对当前目录进行dir(),将结果放入列表 # 将打印信息拼接成字符串,方便后面的not in 判断 rubbish = '' for i in dir: rubbish = rubbish + i # print(rubbish) if fileName_ftp_end not in rubbish: # 不存在则创建文件夹 ftp.mkd(fileName_ftp_end) # 然后进行下载 ftp.cwd(fileName_ftp_end) dir_res = [] files = os.listdir(fileName_ftp) print(files) for i in files: dir_res.append(i) print(dir_res) for name in dir_res: print(fileName_ftp + '/' + name) fd = open(fileName_ftp + '/' + name, 'rb') # 以二进制的形式上传 ftp.storbinary("STOR %s" % name, fd) fd.close() else: ftp.cwd(fileName_ftp_end) dir_res = [] files = os.listdir(fileName_ftp) print(files) for i in files: dir_res.append(i) print(dir_res) # ftp.dir(fileName_ftp_end, dir_res.append) # 对当前目录进行dir(),将结果放入列表 # print(dir_res) for name in dir_res: print(fileName_ftp + '/' + name) fd = open(fileName_ftp + '/' + name, 'rb') # 以二进制的形式上传 ftp.storbinary("STOR %s" % name, fd) fd.close() # 传完文件后再把目录切回原目录,再刷新界面 ftp.cwd('..') # 上传完文件后刷新一遍目录窗口,首先要清空目录,再重载 list2.delete(0, tkinter.END) # 清空列表框 show_ftp() print("upload finished")
(5)提供下载文件服务,主要思路是先获取用户在聊天窗口中选中的文件名称,然后用这个文件名称去FTP服务器进行查找,最后将其下载到指定文件夹中,如果用户选择下载的是文件夹,那就先切换到文件夹中,再依次遍历其中的文件并下载到指定目录中。核心代码如下
def download_ftp(): # 获取到要下载到本地的文件名(只需要名字就行) fileName_ftp = list2.get(list2.curselection()) print(fileName_ftp) # 对文件名做判断 if '.' not in fileName_ftp: # 如果是文件夹的话就切换ftp目录进该文件夹,再下载,最后返回源目录 ftp.cwd(fileName_ftp) dir_res = [] # 将目录内文件信息保存到dir_res中 ftp.dir('.', dir_res.append) # 对当前目录进行dir(),将结果放入列表 # print(dir_res) # 从dir_res中取出文件名放入files数组中 files = [] for i in dir_res: files.append(i.split(' ')[-1]) # print(files) # 设置文件存储路径(路径为文件夹类型) dir_path = tkinter.filedialog.askdirectory() os.mkdir(dir_path + '\\' + fileName_ftp) # print(dir_path_end) for j in files: # 是文件的话就直接下载 # 这里要对目录路径进行一些小替换以符合格式 dir_path_end = dir_path.replace("/", '\\') + '\\' + fileName_ftp + '\\' + j fd = open(dir_path_end, 'wb') # 以二进制形式下载,注意第二个参数是fd.write,上传时是fd ftp.retrbinary("RETR %s" % j, fd.write) fd.close() ftp.cwd('..') else: # 是文件的话就直接下载 # 设置文件存储路径(路径为文件夹类型) dir_path = tkinter.filedialog.askdirectory() # 这里要对目录路径进行一些小替换以符合格式 dir_path_end = dir_path.replace("/", '\\') + '\\' + fileName_ftp print(dir_path_end) fd = open(dir_path_end, 'wb') # 以二进制形式下载,注意第二个参数是fd.write,上传时是fd ftp.retrbinary("RETR %s" % fileName_ftp, fd.write) fd.close() print("download finished")
(6)提供删除FTP服务器内文件服务。主要思路是先获取用户在聊天窗口中选中的文件名称,然后判断文件是单个文件还是文件夹,最后调用相应的FTP函数进行删除。核心代码如下
def delete_ftp(): # 获取到要下载到本地的文件名(只需要名字就行) fileName_ftp = list2.get(list2.curselection()) print(fileName_ftp) # 通过.号判断是否为文件或文件夹,从而赋予不同的颜色 if '.' not in fileName_ftp: # 因为rmd命令只能删空目录 # 所以我们要先递归删除目录里的文件 dir_res = [] ftp.dir(fileName_ftp, dir_res.append) # 对当前目录进行dir(),将结果放入列表 print(dir_res) for i in dir_res: name = i.split(' ')[-1] print(name) ftp.delete(fileName_ftp + '/' + name) ftp.rmd(fileName_ftp) else: ftp.delete(fileName_ftp) print("delete finished") # 上传完文件后刷新一遍目录窗口,首先要清空目录,再重载 list2.delete(0, tkinter.END) # 清空列表框 show_ftp()
(1)首先判断接收到的信息是否抛出异常,如果没有捕获到异常则接收到的是当前在线用户列表,则取出其中的当前在线用户名并依次插入到聊天窗口中。
(2)然后判断接收到的信息的指定位置是否含有特殊词“image”,如果有的话代表是发送图片的命令,则连接上FTP服务器并根据图片名称下载到本地,然后在本地打开该图片并加载到聊天窗口中。
(3)如果以上判断均不成立,则为普通的聊天信息。然后判断聊天对象,最后把聊天内容发送给对应的聊天对象的客户端即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。