import os import struct import json import socket # 设置文件上传路径 BASE_PATH = 'D:python10期每日笔记和视频day01视频' file_list = os.listdir(BASE_PATH) # 创建客户端 client = socket.socket() # 连接服务端 client.connect(('127.0.0.1', 8888)) while True: # 打印文件列表 for file in file_list: print(file_list.index(file), file) # 选择文件 choice = input('>>>:').strip() # 判断输入是否正确 if not choice.isdigit() or int(choice) < 0 or int(choice) >= len(file_list): print('输入有误') continue choice = int(choice) # 拼接文件路径 file_name = file_list[choice] file_path = os.path.join(BASE_PATH, file_name) # 获取文件大小 file_size = os.path.getsize(file_path) # 创建字典 json_dic = {'file_name': file_name, 'file_size': file_size} # 用json将字典序列化 json_dic = json.dumps(json_dic).encode('utf-8') # 用struct打包 header = struct.pack('i', len(json_dic)) # 发送报头 client.send(header) # 发送字典 client.send(json_dic) # 发送文件 sented = 0 print('开始发送文件.....') with open(file_path, 'rb') as f: for line in f: client.send(line) sented += len(line) # 显示进度 print(f' {int((sented/file_size)*100)}%',end='') print('文件发送完毕.....')
import struct import os import json import socket # 创建服务端 server = socket.socket() # 绑定IP和端口 server.bind(('127.0.0.1', 8888)) # 创建半连接池 server.listen(5) while True: # 与客户端建立连接通道,并获取其IP地址 conn, addr = server.accept() while True: try: # 接收报头 msg = conn.recv(4) # 解析报头 header_len = struct.unpack('i', msg)[0] # 接收报头字典 header_dic = conn.recv(header_len) # 反序列化 json_d = json.loads(header_dic.decode('utf-8')) # 获取数据长度及文件名 file_size = json_d.get('file_size') file_name = json_d.get('file_name') # 接收数据,将数据写入文件 data_count = 0 with open(f'upload/{file_name}', 'wb') as f: while data_count < file_size: data = conn.recv(1024) f.write(data) f.flush() data_count += len(data) print('上传成功') except ConnectionResetError: break # 数据接收完毕与客户端断开连接. conn.close()
基于TCP协议,实现客户端与服务端进行文件上传操作.
服务器端与客户端的操作,一定要一一对应.一方是发送状态,另一方必须是读取状态
客户端:
因为TCP是一种流式传输,传数据像流水一样源源不断,不会自动分割,所以,得通过先传递报头,把即将要发送的文件大小,先告之对方,
这时,考虑到发送的文件,可能长度很长,无法用普通方法告知服务端,于引入了字典
将文件大小,文件名等信息,以键传对的形式存入字典,再用json将字典序列化,并用encode编译成二进制,再获取其长度,然后再调用struct包,将字典的长度进行打包,发送给服务端,
再发送二进制形式的json格式字典
然后,考虑到一次性读取文件,可能会导致内存溢出的问题,于是,采用读取一行发送一行的方式,减轻内存压力
服务端:
因为struct的长度相对固定,要么4位,要么8位,这里用4位接收,
接收到后,再用struct解析
获取字典长度
接收字典,再用decode解码,再用json反序列化,再获取内的信息,如文件大小,文件名,等信息.
然后用文件存储接收到的数据,采用接收一行写入一行的方式,减轻内存压力