我们知道上传一个文件的流程原理即将一个文件内容读取出来,在目标位置新建这个文件,再把内容写入进去。想想跟上篇文章 python 网络编程socket示例(远程ssh)类似,所不同的只是多了一个文件操作句柄,同样的也会出现黏包,使用stuct
来解决黏包问题。因为代码中都写有注释,这里就不详细赘述了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| import socket import pickle import os server = socket.socket() ip_port = ('10.10.10.103', 9969) server.bind(ip_port)
server.listen(3) print('服务器运行中………………')
while 1: conn, addr = server.accept() print(addr, '已成功连接至服务器>>>>>') while 1: try: recive_data = pickle.loads(conn.recv(1024)) conn.send(b'200') with open('uploads/'+os.path.basename(recive_data['file_name']), 'wb') as f: data_lens = 0 while data_lens < recive_data['file_size'] : data = conn.recv(1024) data_lens += len(data) f.write(data) percentage = format(data_lens/recive_data['file_size']*100, '.2f') print('文件总大小:{},已上传:{},百分比:{}%'.format(recive_data['file_size'], data_lens, percentage)) print(os.path.basename(recive_data['file_name']),'上传成功!!!') except Exception as e: break print(addr, '已断开连接服务器>>>>>') conn.close() server.close()
import socket import os import pickle client = socket.socket() ip_port = ('10.10.10.103', 9969)
client.connect(ip_port)
while 1: file = input('请输入您要上传的文件:') file_info = {'file_name':file, 'file_size':os.path.getsize(file)} pickle_file = pickle.dumps(file_info) client.send(pickle_file) status_code = client.recv(1024).decode('utf8') if status_code == '200': with open(file, 'rb') as f: for line in f: client.send(line) else: print('上传失败') break client.close()
|
进阶:我们传输文件的流程是先把文件信息传送过去,再把文件内容传送过去,中间利用了recv
接收服务端返回的状态码强制阻塞,分开了两次传输,name我们可不可以直接将文件信息和文件内容一起传过去呢?答案是肯定的。
因为我们可以获取文件信息这个字典的长度,把这个字典的长度也穿过去,大概传过去的数据包样式长这样:【文件信息长度 | 文件信息 | 文件内容】
,看到这种形式其实就已经明白了。我们可以先接收文件信息长度,因为我们使用了struct
,字节长度固定为4,转换后获取到了文件信息的长度,再利用这个文件信息长度,去接受文件信息这一块的内容。文件信息中可是包含文件内容长度的,利用这个长度又可以循环接收文件内容了,这样我们的目的不就达到了嘛:
上面的代码稍微改动即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| import socket import pickle import os import struct server = socket.socket() ip_port = ('10.10.10.103', 9969) server.bind(ip_port)
server.listen(3) print('服务器运行中………………')
while 1: conn, addr = server.accept() print(addr, '已成功连接至服务器>>>>>') while 1: try: file_info_lens = struct.unpack('i', conn.recv(4))[0] print('文件信息长度:',file_info_lens) file_info = pickle.loads(conn.recv(file_info_lens)) print('文件信息:', file_info) file = os.path.basename(file_info['file_name']) file_size = file_info['file_size'] with open('uploads/'+file, 'wb') as f: data_lens = 0 while data_lens < file_size: data = conn.recv(1024) data_lens += len(data) f.write(data) percentage = format(data_lens/file_size*100, '.2f') print('文件总大小:{},已上传:{},百分比:{}%'.format(file_size, data_lens, percentage)) print(os.path.basename(file),'上传成功!!!') except Exception as e: break print(addr, '已断开连接服务器>>>>>') conn.close() server.close()
import socket import os import pickle import struct client = socket.socket() ip_port = ('10.10.10.103', 9969)
client.connect(ip_port)
while 1: file = input('请输入您要上传的文件:') file_info = {'file_name':file, 'file_size':os.path.getsize(file)} pickle_file = pickle.dumps(file_info) pickle_file_lens = struct.pack('i', len(pickle_file)) client.send(pickle_file_lens) client.send(pickle_file) with open(file, 'rb') as f: for line in f: client.send(line) client.close()
|
总结:有些同学会用json
来序列化字典,我们这里使用了pickle
,因为网络通信传输的是字节,json
把字典转成了字符串,你要多一步再转为字节,而利用pickle
直接将字典转为了字节
再加一句:上传看自己是否要做一致性校验,原理就是客户端读取文件每行内容发送时顺便将该行文本追加加密,即md5.update(第一行)
,md5.update(第二行)
……,直至整个文件内容加密完成;服务端接收时也是一行一行的写入新文件,此时也是用md5
进行加密,最后整个文件加密的结果发送给客户端,客户端用两个加密的结果进行比较,相等则代表校验成功