立即学习:https://edu.csdn.net/course/play/24458/296245?utm_source=blogtoedu
1.课程目的:
实现客户端输入下载文件的命令,然后将命令发送给服务端,服务端再执行下载文件的命令,最后将执行下载文件命令后的结果返回给客户端,客户端进行接收,这样就完成了一个简单的文件下载功能。文件的上传与下载类似,只是两个相反的过程
2.知识点与关键点:
1)os模块:可使用os.path.getsize(filename)来获取指定文件的大小;
2)在服务端接收的命令,使用split命令将接收的命令的字符串分割,判断第一个是‘get’还是‘put’,即判断时下载文件还是上传文件
3.完整代码
'''
服务端
![](/qrcode.jpg)
'''
import socket
import subprocess
import json
import struct
import os
server_dir = r'C:\Users\jinlin\Desktop\python_further_study\socket编程\文件的传输(上传)\简单版本\serve'
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
while True:
#接收客户端发送过来连接服务器请求
res = phone.accept()
conn,client_addr = res
while True:
try:
#1接收客户端发送过来的命令
resv = conn.recv(1024)
#2将接收到的结果进行分割,获得命令以及文件名
cmds = resv.decode('utf-8').split()#['get','a.txt']
print('*'*50)
#3处理命令,执行命令并且获得命令得到的结果
cmd = cmds[0]
filename = cmds[1]
total_size = os.path.getsize(r'%s/%s'%(server_dir,filename))#获得文件的字节数大小
#1)制作包含文件名和文件大小的文件头,用字典实现
headers_dict = {
"filename":filename,
"filedata":"2020/03/09",
"total_size":total_size
}
#2)将字典先序列化成惊悚字符串,再转为bytes类型文件头
headers_json = json.dumps(headers_dict)
#3)获取bytes类型的长度
headers_bytes = headers_json.encode('utf-8')
headers_size = len(headers_bytes)
#4)将bytes类型文件头长度定制为固定长度的报头
header = struct.pack('i',headers_size)
#5)向客户端发送报头
conn.send(header)
#6)向客户端发送包含文件信息的字典
conn.send(headers_bytes)
#7)通过二进制只读的方式打开文件,按行读取文件并且发送给客户端
with open(filename,'rb') as fp:
for line in fp:
conn.send(line)
except ConnectionResetError:
break
conn.close()
phone.close()
'''
客户端
'''
#导入模块
import socket
import struct
import json
client_dir = r'C:\Users\jinlin\Desktop\python_further_study\socket编程\文件的传输(上传)\简单版本\client'
#1、设置phone套接字
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、连接服务器(打电话),本地地址:127.0.0.1
phone.connect(('127.0.0.1',8080))
#3、向服务器发送请求send(),发送的数据不能直接发送字符串,因为要传送到物理层底层,因此需要转换成二进制的bytes类型进行发送,只需:发送的数据.encode('utf-8')即可
while True:
cmd = input("请输入:")#向服务端发送下载文件的命令,get a.txt
#修复客户端发送空字符串而服务器卡在接收信息处的bug,continue表示跳出本次循环,重新开始下一次的循环
if not cmd:continue
phone.send(cmd.encode('utf-8'))
#4、接收服务器返回来的数据recv()
#1)先接收由服务器返回来的报头,报头是固定长度的,因此取前面4字节的数据即为报头
header = phone.recv(4)#返回的是一个对象
#2)解析报头,得到bytes类型的文件头长度
obj_truple = struct.unpack('i',header)#返回的是一个元组
headers_bytes_size = obj_truple[0]#取元组第一个元素即为总字节数
#3)接收bytes类型的文件头数据
headers_bytes = phone.recv(headers_bytes_size)
#4)将bytes类型的文件头数据反序列化成字典
headers_json = headers_bytes.decode('utf-8')
headers_dict = json.loads(headers_json)
print(headers_dict)
#5)从字典中取出字命令执行结果字节总长度
total_size = headers_dict['total_size']
filename = headers_dict['filename']
#6)接收返回的数据
with open(r'%s/%s'%(client_dir,filename), 'wb') as fp:
recv_size = 0
while recv_size < total_size:
recv_line = phone.recv(1024)#接收小于1024bytes的数据
fp.write(recv_line)
recv_size += len(recv_line)
print('文件总字节长为%s,已经下载了%s'%(total_size,recv_size))
#5、关闭套接字phone
phone.close()
4.运行的结果:
...
客户端结果
...
请输入:get mn.png
{'filename': 'mn.png', 'filedata': '2020/03/09', 'total_size': 702935}
文件总字节长为702935,已经下载了6
文件总字节长为702935,已经下载了8
文件总字节长为702935,已经下载了237
文件总字节长为702935,已经下载了455
文件总字节长为702935,已经下载了686
文件总字节长为702935,已经下载了851
文件总字节长为702935,已经下载了946
文件总字节长为702935,已经下载了1113
文件总字节长为702935,已经下载了1339
文件总字节长为702935,已经下载了1387
文件总字节长为702935,已经下载了1847
文件总字节长为702935,已经下载了2009
.......
文件总字节长为702935,已经下载了702656
文件总字节长为702935,已经下载了702935
5.待改进
代码的可读性较差,可以通过函数以及面向对象来对其进行优化,增加其代码的可读性