1.整体结构图
2.服务端
2.1server.py
from socket import socket, AF_INET, SOCK_STREAM
from label import Label
from traversalfolder import dirwalkermsg
from sourcesdetails import sources_details
from filereader import file_reader
import time
def information_generator(request_socket, msg):
if not isinstance(msg, bytes):
msg = msg.encode()
request_socket.send(msg)
time.sleep(0.5)
def response_dir(request_socket, request_msg):
dir_name = request_msg.decode().split(' ')[-1]
# 根据客户端请求下载的内容, 调用函数, 生成字节流
msg = dirwalkermsg(dir_name)
try:
while True:
content = next(msg)
information_generator(request_socket, content)
except Exception as e:
print(e)
def response_file(request_socket, request_msg):
filename = request_msg.decode().split(' ')[-1]
for content in file_reader(filename):
information_generator(request_socket, content)
def response_details(request_socket, request_msg):
dirname = request_msg.decode().split(' ')[-1]
# print(dirname)
for content in sources_details(dirname):
information_generator(request_socket, content)
def request_receiver(request_socket):
# 接受客户端消息, 根据请求信息做出回应
request_msg = request_socket.recv(Label.RECVSIZE)
# 客户端必须根据自己的请求,制定请求信息
if Label.REQUEST_DOWNLOAD_FOLDER in request_msg:
response_dir(request_socket, request_msg)
elif Label.REQUEST_DOWNLOAD_FILE in request_msg:
response_file(request_socket, request_msg)
elif Label.REQUEST_QUERY in request_msg:
response_details(request_socket, request_msg)
else:
request_socket.send(Label.RESPONSE_END)
def server():
# 创建套接字
server_socket = socket(AF_INET, SOCK_STREAM)
# 固定端口号
server_socket.bind(("127.0.0.1", 2813))
while True:
# 被动套接字转换为主动套接字
server_socket.listen(128)
# 生成一个面向请求客户端的套接字
request_socket, client_ip = server_socket.accept()
# 调用发送数据函数
request_receiver(request_socket)
# 关闭套接字
request_socket.close()
# server_socket.close()
def main():
server()
if __name__ == '__main__':
main()
2.2 traversalfolder.py
import os
from label import Label
def dirwalker(foldername):
"""
此方法用于遍历目标文件夹
:param foldername:
:return: 一个生成器
"""
# 遍历文件夹,返回生成器
walkers = os.walk(foldername)
for walker in walkers:
dirname = walker[0].replace('\\', '/')
files = walker[2]
# 返回标记后文件夹的名称
yield Label.RESPONSE_DIRNAME+dirname.encode()
for file in files:
file = dirname + '/' + file.replace('\\', '/')
# 返回标记后文件的名称
yield Label.RESPONSE_FILENAME+file.encode()
yield Label.RESPONSE_FILECONTENT_START
with open(file, 'rb') as f:
msg = f.read()
# 返回文件内容
yield msg if msg != b'' else b' '
yield Label.RESPONSE_FILECONTENT_END
def dirwalkermsg(foldername, source=Label.SOURCE):
"""
用于生产字节流信息
:param foldername: 客户端要copy的文件夹名称
:param source: 服务端数据源
:return: 返回一个字节流生成器
"""
foldername = source + '/' + foldername
if not os.path.isdir(foldername):
print(f"{foldername}不存在!")
yield Label.NOTHINGNESS
else:
for msg in dirwalker(foldername):
if isinstance(msg, bytes):
yield msg
else:
yield msg.encode()
# 返回结束信号
yield Label.RESPONSE_END
2.3 sourcesdetails.py
import os
from label import Label
def sources_details(dirname, source=Label.SOURCE):
foldername = source + '/' + dirname
if not os.path.isdir(foldername):
print(f"{foldername}不存在!")
yield Label.NOTHINGNESS
details = os.listdir(foldername)
# print(details)
for detail in details:
# print(detail)
yield Label.SEARCHRESULT+':'.encode()+detail.encode()
yield Label.RESPONSE_END
2.4 filereader.py
import os
from label import Label
def file_reader(filename, source=Label.SOURCE):
file = source + '/' + filename
if os.path.isfile(file):
yield Label.RESPONSE_FILENAME.decode()+filename
yield Label.RESPONSE_FILECONTENT_START
with open(file, 'rb') as f:
content = f.read()
content = content if len(content) > 0 else b' '
yield content
yield Label.RESPONSE_FILECONTENT_END
else:
yield Label.NOTHINGNESS
# 返回结束信号
yield Label.RESPONSE_END
3. 客户端
import socket
import os
import re
from label import Label
dir_download = 'DownLoad'
if not os.path.isdir(dir_download):
os.mkdir(dir_download)
def recv_file_server(client_socket):
"""
向服务端请求要下载的文件夹,
处理服务端返回的数据,完成下载
"""
# 向服务器请求要拷贝的文件夹
dir_name = input("请输入您的请求:")
client_socket.send(dir_name.encode())
try:
while True:
msg = client_socket.recv(Label.RECVSIZE)
# 判断服务端信息是否被标记, 如果被标记, 以及被标记了什么
# 如果被标记为文件夹, 则建立相应文件夹
if Label.RESPONSE_DIRNAME in msg:
mkdir(msg.decode())
# 如果被标记为文件, 则等待服务端发来文件内容
elif Label.RESPONSE_FILENAME in msg:
text = client_socket.recv(Label.RECVSIZE)
# 如果等到的信息被标记为文件内容开始,则循环接收内容,直到接收到文件内容结束退出循环
# 然后写入文件
if Label.RESPONSE_FILECONTENT_START in text:
content = b''
while True:
temp = client_socket.recv(Label.RECVSIZE)
# print(temp)
if Label.RESPONSE_FILECONTENT_END in temp:
# print(temp)
break
content += temp
writefile(msg.decode(), content)
# 如果被标记为查询结果
elif Label.SEARCHRESULT in msg:
print(msg.decode())
# 如果服务端返回Label.NOTHINGNESS, 则说明请求的内容不存在
elif Label.NOTHINGNESS in msg:
print('请求文件夹不存在!')
break
# 接收到结束信息, 退出while True
elif Label.RESPONSE_END in msg:
print('下载完成!')
break
except Exception as e:
print(e)
def mkdir(msg):
"""
创建文件夹
"""
dirname = re.sub(Label.SOURCE, '', msg)
# print(msg)
dirname = re.sub(Label.RESPONSE_DIRNAME.decode(), '', dirname)
# print(os.getcwd())
# print(dirname)
# 如果文件夹不存在则新建文件夹
if not os.path.isdir(os.getcwd() + '\\' + dir_download + dirname):
os.mkdir(os.getcwd() + '\\' + dir_download + dirname)
print(f'创建文件夹{dirname}')
def writefile(filename, context):
"""
创建文件
"""
filename = re.sub(Label.SOURCE, '', filename)
filename = re.sub(Label.RESPONSE_FILENAME.decode(), '', filename)
with open(os.getcwd() + '/' + dir_download + '/' + filename, 'wb') as f:
f.write(context)
print(f'创建文件{filename}')
def main():
# 创建套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
client_socket.connect(("127.0.0.1", 2813))
# 调用接收函数
recv_file_server(client_socket)
# 关闭套接字
client_socket.close()
if __name__ == '__main__':
main()
4. 客户端与服务端必须采用相同的标记
label.py
class Label(object):
# Response数据中如果意外含有标记信息,会导致不可预料的错误,所以设置n,以避免这种不可预料的错误
n = 3
# 指定传输文件大小
RECVSIZE = 1024 * 1024 * 10
# 数据源
SOURCE = 'sources'
# 文件夹名字字节流标记
RESPONSE_DIRNAME = b'DirName'*n
# 文件名字字节流标记
RESPONSE_FILENAME = b'FileName'*n
# 文件内容开始
RESPONSE_FILECONTENT_START = b'FileContentStart'*n
# 文件内容结束
RESPONSE_FILECONTENT_END = b'FileContentEnd'*n
# 请求内容全部传输完成
RESPONSE_END = b'ResponseEnd!'*n
# 下载文件夹请求
REQUEST_DOWNLOAD_FOLDER = b'RequestDownLoadFolder'
# 下载文件请求
REQUEST_DOWNLOAD_FILE = b'RequestDownLoadFile'
# 查询请求
REQUEST_QUERY = b'RequestQuery'
# 文件或文件夹不存在
NOTHINGNESS = b'None!'*n
# 查询结果
SEARCHRESULT = b'SearchResult'*n
5.命令示例
RequestDownLoadFile test.py
RequestDownLoadFolder sales
RequestQuery sales
如果输入指令有问题,客户端会直接退出服务。