day29 python socket实现文件上传下载功能 socketserver模块
day29 python socket实现文件上传下载功能 socketserver模块
一.写个文件上传的功能(类似于ftp文件上传的功能)
1.上传文件需要考虑先传: 文件信息(包括文件名, 大小, md5, 和上传的动作)
2.上传数据结构的组织: 防止出现黏包的现象(通过构造数据头部信息实现)
上传的数据格式为: json_header+json+data
json_header: 此字段固定长度为4字节(由struct模块实现), 用于取到完整的json
json:此为记录文件信息,记录文件的各种信息
data:通过json中文件长度,取文件的全部数据
#ftp_server
import socket
import json
import struct
import hashlib
ip_port = ('127.0.0.1', 65533)
server = socket.socket()
server.bind(ip_port)
server.listen(5)
print('ftp_server is running... ...')
while 1:
conn, addr = server.accept()
while 1:
json_header = conn.recv(4)
json_header = struct.unpack('i', json_header)[0]
file_info_json = conn.recv(json_header)
file_info_dict = json.loads(file_info_json)
file_name = file_info_dict.get('file_name')
file_size = file_info_dict.get('file_size')
file_md5 = file_info_dict.get('file_md5')
file_operation = file_info_dict.get('file_operation')
if file_operation == 'put':
recv_size = 0
md5 = hashlib.md5(b'bajieaishuishui')
with open('abc'+file_name, mode='wb') as f: #出现过的问题: 文件19k, 但是接收的文件只有1k,(md5对的: 说明接收到的数据是完整的, 只是写文件的过程中有问题)
while recv_size < file_size: #原因: 是把条件判断写在了文件打开的外面, 导致每次条件判断都先清空文件.最后只留下最后一次接收
free_size = file_size - recv_size #解决方法: 条件判断和打开文件动作互换, 得以解决
if free_size < 1024:
recv_data = conn.recv(free_size)
else:
recv_data = conn.recv(1024)
recv_size += len(recv_data)
free_size = file_size - recv_size
f.write(recv_data)
md5.update(recv_data)
recv_md5 = md5.hexdigest()
if file_md5 == recv_md5:
conn.send(b'203')
else:
conn.send(b'204')
#上传
elif file_operation == 'get':
#下载
pass
#ftp_client
import socket
import json
import struct
import os
import hashlib
ip_port = ('127.0.0.1', 65533)
def mymd5(filename):
md5 = hashlib.md5(b'bajieaishuishui')
with open(filename, mode='rb') as f:
for line in f:
md5.update(line)
else:
return md5.hexdigest()
client = socket.socket()
client.connect(ip_port)
while 1:
cmds = input('>>> ') #命令: put digit.py
file_name = cmds.strip().split(' ')[1]
file_operation = cmds.strip().split(' ')[0]
file_size = os.path.getsize(file_name)
file_md5 = mymd5(file_name)
file_info_dict = {'file_name':file_name,
'file_operation':file_operation,
'file_size':file_size,
'file_md5':file_md5,
}
file_info_json = json.dumps(file_info_dict)
json_size = len(file_info_json)
json_header = struct.pack('i',json_size)
client.send(json_header)
client.send(file_info_json.encode('utf-8'))
with open(file_name, mode='rb') as f:
for line in f:
client.send(line)
code = client.recv(1024).decode('utf-8')
if code == '203':
print('full send')
elif code == '204':
print('send error')
二.socketserver模块
1.socketserver的使用
多继承: 灵活
import socketserver
ip_port = ('127.0.0.1',65533)
class Myserver(socketserver.BaseRequestHandler): #自定义一个类
def handle(self): #方法名必须是这个名字
print('socketserver is runing... ...') #这里是业务逻辑: 而且 conn 会封给 self.request
while 1:
try:
data = self.request.recv(1024)
self.request.send(data+b'bajieaishuishui')
except:
self.request.close()
server = socketserver.ThreadingTCPServer(ip_port, Myserver) #socket.socket, bind, listen
server.serve_forever() #accept
2.解析socketserver的源码
https://www.processon.com/diagraming/5d832015e4b011ca2aa63171
三.ftp练习
1.多用户同时登陆 (并发, 用socketserver模块实现)
2.用户登录,加密认证 (使用hashlib MD5加密)
3.上传/下载文件, 保证文件的一致性 (对文件hashlib md5加密)
4.传输过程中实现进度条
5.不同用户家目录不同, 且只能访问自己的家目录 (os.path.join('',username, 'a.txt')
6.对用户进行磁盘配额, 不同用户配额可不同 (上传,下载之前做文件夹大小的判断)
7.用户登录server后, 可在家目录权限下切换子目录 (客户端向服务端发送命令, 服务端执行命令 subprocess.popen)
8.查看当前目录下文件, 新建文件夹 (dir )
9.删除文件和空文件夹 (也是执行命令)
10.充分使用面向对象知识 (类,反射)
11.支持断点续传 ()