利用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()

ftp_socket_server1_simple.py

#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()

ftp_socket_client1_simple.py

输出结果

[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''
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关键代码

另外,由于服务端先发送了文件,再发送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()

ftp_socket_server2_md5.py

#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()

ftp_socket_client2_md5.py

注意:使用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''
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]#

输出结果

最新文章

  1. Spring之ClassPathResource加载资源文件
  2. 2016年中国大学生程序设计竞赛(合肥)-重现赛1008 HDU 5968
  3. 并发编程 19—— 显式的Conditon 对象
  4. C#函数式编程之缓存技术
  5. (三)结构体指针、sizeof
  6. Eclipse中添加web dynamic project
  7. Powershell导入订阅号(以Azure中国版为例)
  8. Chromium浏览器高级开发系列第一篇:如何获取最新chromium源码
  9. HDU 1160 FatMouse&#39;s Speed(DP)
  10. 【原】YUI3:js加载过程及时序问题
  11. fckeditor使用详解
  12. Python 文本和字节序列
  13. netty 入门(一)
  14. hadoop上C++开发两种方式的例子
  15. InnoDB: Error: Table &quot;mysql&quot;.&quot;innodb_table_stats&quot; not found.
  16. Pfsense2.34中文版
  17. Python自然语言处理笔记【一】文本分类之监督式分类
  18. 原生js实现九宫格,全解析
  19. 服务发现比较:Consul vs Zookeeper vs Etcd vs Eureka
  20. Educational Codeforces Round 44 (Rated for Div. 2)

热门文章

  1. tensorboard基础使用
  2. 【异常处理】Java异常如何做异常处理
  3. springboot集成mybatis源码分析-启动加载mybatis过程(二)
  4. Flask上下文管理源码--亲自解析一下
  5. Hibernate Envers
  6. 金蝶K/3 固定置产相关SQL语句
  7. 华为交换机批量加入 Vlan 方法
  8. Linux 目录结构和常用命令
  9. [转]Windows下Python多版本共存
  10. python3 解析 base64 数据