基于之前解决粘包问题后,来实现文件传输我们就不用subprocess模块了,要传输文件首先需要打开文件以字节的格式传给对方,再让对方解码得到文件。但在这之前,我们还需要制定一下规则,确定一下客户端需要什么文件。他发给服务端的请求格式。比如:
cmd = input('>>:').strip() # get a.txt
if not cmd:
continue
phone.send(cmd.encode("gbk"))
客户端输入‘get a.txt’给服务端,服务端需要把这个消息分成两部分来解读,第一部分是get,表示客户端想要得到;第二部分是a.txt,指的是文件的名称。比如:
# 收命令
res = conn.recv(8096) # b'get a.txt'
# 解析命令、提取相应的命令参数
cmds = res.decode('gbk').split() # ['get','a.txt'] split变列表格式
filename = cmds[1]
这里服务端用split把‘get a.txt’分开变成列表。这样我们就知道了客户端需要什么文件,然后再自己库中找到文件发送给客户端。
最后,配合上之前的内容:
1、客户端
import socket
import struct # 制作报头的模块
import json # 转换数据格式(序列化)
import os
share_dir = ''# 这里是服务器储存资源的地址
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(('127.0.0.1', 8848))
phone.listen(5)
while True:
conn, client = phone.accept()
while True:
try:
# 收命令
res = conn.recv(8096) # b'get a.txt'
# 解析命令、提取相应的命令参数
cmds = res.decode('gbk').split() # ['get','a.txt'] split变列表格式
filename = cmds[1]
# 已读的方式打开文件,读取文件内容发送给客户端
# 第一步:制作固定长度的报头
header_dic = {
'filename': filename,
'file_size': os.path.getsize(r'%s/%s' % (share_dir, filename)) # 这里把文件的名字和地址结合在一起得到文件长度
} # 字典方便储存数据
header_json = json.dumps(header_dic) # 把字典转换成js格式(字符串类型)
header_bytes = header_json.encode('gbk')
# 第二步:先发送报头的长度
conn.send(struct.pack('i', len(header_bytes)))
# 第三步:再发报头
conn.send(header_bytes)
# 第四步:发送真实数据
with open('%s/%s' % (share_dir, filename), 'rb') as f:
for line in f: # 这样一行一行发比直接发送f.read节省内存空间
conn.send(line)
except ConnectionResetError as err:
break
conn.close()
phone.close()
2、客户端
import socket
import struct
import json
download_dir = '' # 这里是客户端存放资源的地址
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8848))
print("------开始咨询客服---------")
while True:
# 发命令
cmd = input('>>:').strip() # get a.txt
if not cmd:
continue
phone.send(cmd.encode("gbk"))
# 已写的方式打开一个新文件,接收服务器发来的文件的内容写入客户端的新文件
# 第一步:先收取报头的长度
obj = phone.recv(4)
header_size = struct.unpack('i', obj)[0]
# 第二步:再收报头
header_bytes = phone.recv(header_size)
# 第三步:从报头中间解析出对真是数据的描述信息
header_json = header_bytes.decode('gbk')
header_dic = json.loads(header_json)
print(header_dic)
total_size = header_dic['file_size']
filename = header_dic['filename']
# 第三步:接受真实的数据
with open('%s/%s' % (download_dir, filename), 'wb') as f: # 在自己的电脑中找一个地址打开一个同类型的文件准备接收数据
recv_size = 0
while recv_size < total_size:
line = phone.recv(1024)
f.write(line)
recv_size += len(line)
print('总大小:%s 已下载大小:%s' % (total_size, recv_size)) # 显示下载进度
phone.close()
这里只有一个下载功能,要想客户端上传文件数据业绩就是对调一下,没什么大问题。熟练后可以用函数或者类的方法来重写。