立即学习:https://edu.csdn.net/course/play/24458/296245?utm_source=blogtoedu
1.课程目的:
实现客户端输入下载文件的命令,然后将命令发送给服务端,服务端再执行下载文件的命令,最后将执行下载文件命令后的结果返回给客户端,客户端进行接收,这样就完成了一个简单的文件下载功能。文件的上传与下载类似,只是两个相反的过程
2.知识点与关键点:
1)os模块:可使用os.path.getsize(filename)来获取指定文件的大小;
2)在服务端接收的命令,使用split命令将接收的命令的字符串分割,判断第一个是‘get’还是‘put’,即判断时下载文件还是上传文件
3.完整代码
'''
服务端
'''
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 = reswhile 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:breakconn.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:continuephone.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 = 0while 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.待改进
代码的可读性较差,可以通过函数以及面向对象来对其进行优化,增加其代码的可读性