利用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]#

输出结果

Python3学习之路~8.4 利用socket实现文件传送+MD5校验的更多相关文章

  1. Python3学习之路~0 目录

    目录 Python3学习之路~2.1 列表.元组操作 Python3学习之路~2.2 简单的购物车程序 Python3学习之路~2.3 字符串操作 Python3学习之路~2.4 字典操作 Pytho ...

  2. Python3学习之路~8.2 socket简单实例 实现ssh 发送大量数据

    实例1: 利用socket模拟客户端和服务器端各自收发一次数据: #Author:Zheng Na # 客户端 import socket # 声明socket类型,同时生成socket连接对象 cl ...

  3. Python socket文件传送md5校验

    soket_server import socket,os,hashlib server = socket.socket() server.bind(('0.0.0.0',9999)) server. ...

  4. Python3学习之路~8.1 socket概念及参数介绍

    一 socket介绍 TCP/IP 基于TCP/IP协议栈的网络编程是最基本的网络编程方式,主要是使用各种编程语言,利用操作系统提供的套接字网络编程接口,直接开发各种网络应用程序. socket概念 ...

  5. Python3学习之路~8.3 socket 服务端与客户端

    通过8.2的实例1-6,我们可以总结出来,socket的服务端和客户端的一般建立步骤: 服务端 步骤:1创建实例,2绑定,3监听,4阻塞,5发送&接收数据,6关闭. #Author:Zheng ...

  6. Python3学习之路~8.6 开发一个支持多用户在线的FTP程序-代码实现

    作业: 开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...

  7. Python3学习之路~8.5 SocketServer实现多并发

    前面几节我们写的socket都只能实现服务端与一个客户端通信,并不能实现服务端与多客户端同时通信.接下来我们就来学习一下如何实现服务端同时与多个客户端通信,即并发. Socket Server soc ...

  8. Python3学习之路~10.2 协程、Greenlet、Gevent

    一 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切 ...

  9. Python3学习之路~9.4 队列、生产者消费者模型

    一 队列queue 当必须在多个线程之间安全地交换信息时,队列在线程编程中特别有用. 队列的作用:1.解耦,使程序直接实现松耦合 2.提高处理效率 列表与队列都是有顺序的,但是他们之间有一个很大的区别 ...

随机推荐

  1. vue路由懒加载

    大项目中,为了提高初始化页面的效率,路由一般使用懒加载模式,一共三种实现方式.(1)第一种写法: component: (resolve) => require(['@/components/O ...

  2. select获取选中的option(包含value和text,重点是text怎么获取)

    简单描述:后台需要获取到select标签选择的内容,也就是text,该怎么取呢?很简单. 代码: //hml代码<div class="col-md-6"> <l ...

  3. CSS选择器权重计算

    CSS各种选择器的权重: 1.ID选择器  +100 2.类.属性.伪类选择器   +10 3.元素.伪元素选择器   +1 4.其他选择器   +0 如果有两个CSS样式都作用于某元素,如: #id ...

  4. Python3 元组(tuple)

    一.定义:不可变序列的数据元素集合,元组的元素是不可以修改的 元组使用小括号,例如:tuple = (1,) 注意:即使元组里面只有一个元素,该元素后面也要加 ",":在函数传递参 ...

  5. 学习笔记: MD5/DES/RSA三类加密,SSL协议解析

    1. 不对称可逆加密的 的2种用法 (1)保证信息不被篡改 (2) 保证信息只能被我看到 2. CA证书的基本原理 流程如下:  百度公司 向CA机构报备 持有者姓名, 有效期, 要发布的公钥 , 扩 ...

  6. 不装插件,查看.rp文件

    AxShare国内平台地址:http://share.axure.org临时急用可以使用公用帐户.用户名:axure@webppd.com,密码:webppd123 上传.rp文件,点击 url 地址 ...

  7. php页面传值的方法(转)

    原文链接:https://www.cnblogs.com/suvllian/p/5582540.html   PHP页面间传值的几种方法 方法一:require_once //Page a: < ...

  8. ECMAScript6 入门-let与const命令

    块级作用域 1:let命令声明的变量只在let命令所在的代码块有效--简而言之 大括号既是代码块,也就是说存在块级作用域了. { let a =10; var b =1; } a // Referen ...

  9. jsp填坑:找不到属性

    javax.el.PropertyNotFoundException: Property [***] not found on type 接手的项目的页面是用jsp写的,虽然再有十几天就2019年了, ...

  10. SDOI2018:荣誉称号

    题解: https://files.cnblogs.com/files/clrs97/title-solution.pdf Code: #include<cstdio> #include& ...