Python多人聊天室

目录

第一章 项目简介及源代码链接

        1.1 源代码链接

        1.2 项目基本功能

第二章 功能演示

第三章 模块化分析

        3.1 服务端

                3.1.1 服务端基本设置

                3.1.2 聊天服务器主要模块

                3.1.3 FTP服务器设置模块

        3.2 客户端

                3.2.1 初始登录模块

                3.2.2 聊天窗口模块

                3.2.3 图片发送模块

                3.2.4显示当前在线用户模块

                3.2.5与服务器通信模块

                3.2.6私聊模块

                3.2.7邮件功能模块

                3.2.8 FTP功能模块

                3.2.9接收服务端发来的信息模块

第一章 项目简介及源代码链接

1.1 源代码链接

https://github.com/chenaaa1/-python.git

1.2 项目基本功能

  1. 利用socket方式编写一个多人聊天室程序,可以实现多个用户之间的群聊功能,私聊功能,显示当前用户功能
  2. 在聊天室程序中增加利用ftp实现文件的上传,下载,删除,查看当前文件功能
  3. 在聊天室程序中增加利用ftp实现在聊天过程中可以发送图片功能
  4. 在聊天室程序中增加发送邮件功能,并提供附件发送服务

第二章 功能演示

  1. 设置好服务端的配置代码

  2. 登录界面 

  3. 使用过程

第三章 模块化分析

3.1 服务端

服务端主要用于为客户端提供聊天服务和FTP服务,所以我分别创建了两个类为ChatServer和FtpServer

3.1.1 服务端基本设置

对于服务端的代码分析,首先是要让用户对服务器的基本信息进行设置,具体设置代码如下

# ----------------------------------------文件传输服务器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--------------------------

3.1.2 聊天服务器主要模块

  1. TCP连接模块,主要代码就是与客户端建立起TCP通信
  2. 当前在线用户模块,主要就对当前连接的客户端数目进行判断,然后返回一个当前在线用户数
  3. 接收信息模块,主要代码是接收客户端发来的信息,然后存入一个队列中
  4. 发送信息模块,主要代码是对客户端发来的信息进行分析,分析完后转入对于的函数去执行相关操作,比如接收到的是聊天信息就发到聊天用户的客户端中,接收到的是图片信息就发送指定字符串给聊天用户的客户端,然后对指定图片执行FTP上传下载操作。
  5. 服务器启动检查模块,主要是令服务端与客户端进行连接,然后在控制台内打印连接状况的信息。

3.1.3 FTP服务器设置模块

对于FTP服务器,主要代码是用于初始化FTP服务器,如设置FTP的用户,密码和FTP的文件夹,具体如下图所示

# ----------------------------------------文件传输服务器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--------------------------

3.2 客户端

3.2.1 初始登录模块

初始登录模块,即用tkinter库创建出一个界面,再在里面添加输入框,最后将用户输入的信息保存为对应的全局变量给后续代码使用。核心代码如下所示

# 登录按钮

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()  # 关闭登录窗口

3.2.2 聊天窗口模块

聊天窗口模块,即登录成功后为用户显示出来的窗口,用于用户聊天和进行其他操作,主要也是利用了tkinter库来创建出对应的界面

3.2.3 图片发送模块

(1)先进行FTP登录,方便后续把图片发送到服务器中
(2)打开本地文件资源管理器,找到图片的位置,修改图片的大小,因为聊天窗口无法加载过大的图片 (3)利用FTP把图片上传到服务器中
(4)给服务器发送一个指定的消息,表示这是一个发送图片命令

3.2.4显示当前在线用户模块

主要原理是先接收服务端发来的当前在线用户数,然后插入到聊天窗口中即可。

3.2.5与服务器通信模块

主要思路是先根据窗口信息确定聊天对象,然后再根据用户输入信息确定聊天内容,最后将这些信息加上分隔符后拼接成字符串,然后发送给服务端。核心代码如下

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('')  # 发送后清空文本框

3.2.6私聊模块

主要思路是确定好聊天的两个对象,然后将用户和其聊天对象名拼接成字符串显示到聊天框中,方便用户确定当前聊天对象,核心代码如下

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)

3.2.7邮件功能模块

主要思路如下

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 = ''

3.2.8 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')

(1)提供文件上传或文件夹上传服务,如果是文件就直接调用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")

(2)提供下载文件服务,主要思路是先获取用户在聊天窗口中选中的文件名称,然后用这个文件名称去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")

(3)提供删除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()

3.2.9接收服务端发来的信息模块

(1)首先判断接收到的信息是否抛出异常,如果没有捕获到异常则接收到的是当前在线用户列表,则取出其中的当前在线用户名并依次插入到聊天窗口中。
(2)然后判断接收到的信息的指定位置是否含有特殊词“image”,如果有的话代表是发送图片的命令,则连接上FTP服务器并根据图片名称下载到本地,然后在本地打开该图片并加载到聊天窗口中。
(3)如果以上判断均不成立,则为普通的聊天信息。然后判断聊天对象,最后把聊天内容发送给对应的聊天对象的客户端即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值