利用socket实现文件传送,大约分为如下几步:
1.读取文件名
2.检测文件是否存在
3.打开文件(别忘了最后关闭文件)
4.检测文件大小
5.发送文件大小给客户端
6.等客户端确认
7.开始边读边发数据
8.md5验证
实例1:实现步骤1-7
运行代码
#Author:Zheng Na #ftp服务端 import socket import os server = socket.socket() server.bind(('localhost',6969)) server.listen() while True: print("等待新客户端连接") conn,addr = server.accept() print("new conn: ",addr) while True: print("等待新指令") data = conn.recv(1024) if not data: print("客户端已断开") break cmd,filename = data.decode().split() # 1.读取文件名 print("文件名:",filename) if os.path.isfile(filename): # 2.判断文件是否存在 f = open(filename,'rb') # 3.打开文件 file_size = os.stat(filename).st_size # 4.检测文件大小 conn.send(str(file_size).encode()) # 5.发送文件大小给客户端 print("等待客户ack应答...") client_final_ack = conn.recv(1024) # 6.等待客户端确认 print("客户应答:", client_final_ack.decode("UTF-8")) for line in f: # 7.开始边读边发数据 conn.send(line) f.close() # 关闭文件 print("send done") server.close()
#Author:Zheng Na #ftp客户端 import socket client = socket.socket() client.connect(('localhost',6969)) while True: cmd = input(">>: ").strip() if len(cmd) == 0: continue if cmd.startswith("get"): client.send(cmd.encode()) server_response = client.recv(1024) print("server response: ",server_response) client.send(b'ready to recv file') file_total_size = int(server_response.decode()) received_size = 0 filename = cmd.split()[1] f = open(filename+'.new','wb') while received_size < file_total_size: data = client.recv(1024) received_size += len(data) f.write(data) # print(file_total_size,received_size) else: print("file recv done",file_total_size,received_size) f.close() client.close()
输出结果
[root@hadoop my-test-files]# python3 ftp_socket_server1_simple.py 等待新客户端连接 new conn: ('127.0.0.1', 34556) 等待新指令 文件名: test.txt 等待客户ack应答... 客户应答: ready to recv file send done 等待新指令 [root@hadoop my-test-files]# python3 ftp_socket_client1_simple.py >>: get test.txt server response: b'5028317' file recv done 5028317 5028317 >>: [root@hadoop my-test-files]# ll test* -rw-r--r-- 1 root root 5028317 12月 5 18:28 test.txt -rw-r--r-- 1 root root 5028317 12月 6 16:16 test.txt.new [root@hadoop my-test-files]# diff test.txt test.txt.new # 比较两个文件是否相同 [root@hadoop my-test-files]#
实例2:实现步骤8:md5验证
接下来,我们来给传送的文件进行MD5验证。
思路:
在服务端对要发送的文件进行MD5加密,得到一个16进制格式的hash1,发送给客户端;
在客户端对接收到的文件进行MD5加密,得到一个hash2,并与hash1进行对比,如果相同,说明接收到的文件与服务器文件一致。
由于文件比较大,可以打开文件后一行一行的进行md5加密,这样操作与整个文件一起加密效果是一致的。
比如下面两段代码效果是一致的:
# 代码1 import hashlib hash = hashlib.md5() hash.update(b'Hello ') hash.update(b'World') print(hash.hexdigest()) # b10a8db164e0754105b7a99be72e3fe5 # 代码2 import hashlib hash = hashlib.md5() hash.update(b'Hello World') print(hash.hexdigest()) # b10a8db164e0754105b7a99be72e3fe5
另外,由于服务端先发送了文件,再发送md5,为了防止两次send之间的粘包,我们可以在两次send之间加一段 客户确认 的代码,但是这次我们不这么做:
由于在客户端接收文件时是循环接收,我们可以在接收文件前,加一个判断,判断最后一次接收文件的大小,如果小于1024字节,那么最后一次只接收文件最后剩下的部分。
这样就可以保证最后接收的文件正好是发送文件的大小,从而不会导致粘包。
运行代码
#Author:Zheng Na #ftp服务端 import socket import os import hashlib server = socket.socket() server.bind(('localhost',6969)) server.listen() while True: print("等待新客户端连接") conn,addr = server.accept() print("new conn: ",addr) while True: print("等待新指令") data = conn.recv(1024) if not data: print("客户端已断开") break cmd,filename = data.decode().split() # 1.读取文件名 print("文件名:",filename) if os.path.isfile(filename): # 2.判断文件是否存在 f = open(filename,'rb') # 3.打开文件 file_size = os.stat(filename).st_size # 4.检测文件大小 conn.send(str(file_size).encode()) # 5.发送文件大小给客户端 print("等待客户ack应答...") client_final_ack = conn.recv(1024) # 6.等待客户端确认 print("客户应答:", client_final_ack.decode("UTF-8")) m = hashlib.md5() # 8.1 md5 for line in f: # 7.开始边读边发数据 m.update(line) # 8.2 md5 conn.send(line) server_file_md5 = m.hexdigest() # 8.3 md5 print("server file md5: ",server_file_md5) conn.send(server_file_md5.encode()) # 8.4 md5 f.close() # 关闭文件 print("send done") server.close()
#Author:Zheng Na #ftp客户端 import socket import hashlib client = socket.socket() client.connect(('localhost',6969)) while True: cmd = input(">>: ").strip() if len(cmd) == 0: continue if cmd.startswith("get"): client.send(cmd.encode()) server_response = client.recv(1024) print("server response: ",server_response) client.send(b'ready to recv file') file_total_size = int(server_response.decode()) received_size = 0 filename = cmd.split()[1] f = open(filename+'.new','wb') m = hashlib.md5() while received_size < file_total_size: # 接收文件,防止粘包 if file_total_size - received_size > 1024: # 要收的不止一次 size = 1024 else:# 最后一次了,剩多少收多少 size = file_total_size -received_size print("last receive:",size) data = client.recv(size) received_size += len(data) m.update(data) f.write(data) # print(file_total_size,received_size) else: client_file_md5 = m.hexdigest() print("file recv done",file_total_size,received_size) f.close() server_file_md5 = client.recv(1024).decode() print("server file md5: ",server_file_md5) print("client file md5: ",client_file_md5) if server_file_md5 == client_file_md5: print("server file md5 and client file md5 are same") else: print("server file md5 and client file md5 are different") client.close()
注意:使用md5加密,运行结果会变慢。
输出结果
[root@hadoop my-test-files]# python3 ftp_socket_server2_md5.py 等待新客户端连接 new conn: ('127.0.0.1', 34566) 等待新指令 文件名: test.txt 等待客户ack应答... 客户应答: ready to recv file server file md5: 01e16e921c663c9e90246ddb7d9a746f send done 等待新指令 [root@hadoop my-test-files]# python3 ftp_socket_client2_md5.py >>: get test.txt server response: b'5028317' last receive: 335 file recv done 5028317 5028317 server file md5: 01e16e921c663c9e90246ddb7d9a746f client file md5: 01e16e921c663c9e90246ddb7d9a746f server file md5 and client file md5 are same >>: [root@hadoop my-test-files]# ll test* -rw-r--r-- 1 root root 5028317 12月 5 18:28 test.txt -rw-r--r-- 1 root root 5028317 12月 6 17:44 test.txt.new [root@hadoop my-test-files]# diff test.txt test.txt.new [root@hadoop my-test-files]#