[原创]python之socket-ftp
今天来讲讲ftp文件下载,感觉挺有趣的,知道吧,就那种看到新文件生成,而自己写的代码也不多,那种成就感!
一、需求:
客户端发送指令给服务端,服务端根据指令找到相应文件,发送给客户端
分析:
PS:encode() decode()默认是utf-8
Ftp server
1.读取文件名
2.检测文件是否存在
3.打开文件
4.检测文件大小
5.发送文件大小给客户端
6.等客户端确认 #防止粘包
7.开始边读边发数据
8.发送md5
客户端的md5与服务器端的md5对比,相同即文件传输过程没有改变
二、知识铺垫
旧的知识不用就会忘记,Come On! 正式讲ftp前先看下面的两个点:
1. os.path.isfile(path)
如果path是一个存在的文件,则返回True, 否则返回False
注:运行文件是test.py, test_bb.py与test.py在同一个目录下
import os
a = os.path.isfile("E:\\hello.py")
print(a)
b = os.path.isfile("hello.py")
print(b)
c = os.path.isfile("test_bb.py")
print(c)
运行结果:
True
False
True
上面是我自己测试的,可以总结一下:
os.path.isfile(path) 中path是一个路径下的文件;也可以是与测试文件在同一目录下的文件名
2.md5
做了下面的测试,m是md5对象,最后输出结果是一样的,意味着一点一点加密与一起加密最后的结果是一样的,为什么要这么做??因为如果要传输的文件几G,那肯定是一行一行传输的,一行一行加密的。

三、开始打码
服务端:
import socket
import os
import hashlib server = socket.socket()
server.bind(("localhost", 9998)) server.listen(5) while True:
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()
if os.path.isfile(filename): #如果是文件
f = open(filename, "rb")
m = hashlib.md5() # 创建md5对象
file_size = os.stat(filename).st_size #获取文件大小
conn.send(str(file_size).encode()) #发送文件大小
conn.recv(1024) #接收客户端确认
for line in f:
conn.send(line) #发送数据
m.update(line)
print(cmd, filename)
print("file md5", m.hexdigest())
f.close()
conn.send(m.hexdigest().encode()) #发送md5 server.close()
客户端:
import socket
import hashlib client = socket.socket() client.connect(("localhost", 9998)) while True:
cmd = input(">>>:").strip()
if len(cmd) == 0:
continue
if cmd.startswith("get"):
client.send(cmd.encode()) #客户端发送指令
receive_file_size = client.recv(1024)
print("server file size",receive_file_size.decode())
client.send("准备好接收文件了".encode()) #客户端发送确认 receive_size = 0
file_total_size = int(receive_file_size.decode())
filename = cmd.split()[1]
f = open(filename + ".new", "wb") #新文件,没有的话会创建
m = hashlib.md5() #生成md5对象 while receive_size < file_total_size:
data = client.recv(1024)
receive_size += len(data)
m.update(data)
f.write(data) #写到文件
else:
new_file_md5 = m.hexdigest() #根据收到文件生成的md5
print("file recv done")
print("receive_size:", receive_size)
print("total_file_size:", file_total_size)
f.close()
receive_file_md5 = client.recv(1024)
print("server file md5:", receive_file_md5)
print("client file md5:", new_file_md5) client.close()
如果看不大懂,可以先看我上一篇博文socket-ssh。
OK, 这样就可以了,可以了吗?吗?废话不多说,测试一下:
客户端:
C:\Python34\python3.exe C:/Users/Administrator/PycharmProjects/laonanhai/day9/ftp_client.py
>>>:get test.py
server file size 477
file recv done
receive_size: 477
total_file_size: 477
server file md5: b'18e84dd5d7b345db59526b1a35d07ef2'
client file md5: 18e84dd5d7b345db59526b1a35d07ef2
>>>:get tes
server file size 39
file recv done
receive_size: 71
total_file_size: 39 #天呐,客户端卡住了!!
服务端:
C:\Python34\python3.exe C:/Users/Administrator/PycharmProjects/laonanhai/day9/ftp_server.py
new conn: ('127.0.0.1', 62944)
等待新指令
get test.py
file md5 18e84dd5d7b345db59526b1a35d07ef2
等待新指令
get tes
file md5 c21eff88569c5ca26d8019bd891c27e9
等待新指令
四、问题分析
看了上面的例子,觉得你们应该给我一个赞,我可是搞了很久,才终于搞出一个有粘包的测试。
OK,分析一下上面的问题。
客户端执行第一个命令是正常的,但是执行get tes就卡住了。为什么呢??再分析一下,服务端发给客户端的文件大小是39个字节,但是客户端收到的却是71个字节,My God,这意味着什么?很有可能粘包不是么!啥,你们不信,好。爸爸证明给你们看:
首先,打开原tes文件:
rgioknjt
bjfoikvj
bkitjmbvki
gvbtj
再打开运行后生成的新文件tes.new:
rgioknjt
bjfoikvj
bkitjmbvki
gvbtj
c21eff88569c5ca26d8019bd891c27e9
什么情况?tes.new多了一行,怎么那么像md5? OK,打开服务端看下,My God!多了的一行就是服务器端(因为粘包)发给客户端的md5,而客户端没有收到服务端的md5,就一直卡在 receive_file_md5 = client.recv(1024) 这里。
服务端conn.send(line),接着又send(m.hexdigest())有可能产生粘包
五、ftp优化
知道BUG所在后,怎么优化改进呢?客户端再给服务器一次响应??但你不会觉得这样来回交互太麻烦吗?
接下来提供一个方法:
客户端已经知道接收多少数据,那让客户端接收文件时正好接收这些数据就可以了。EG:原本是收5M,但服务端发了5.1M,多的0.1M是md5,那在循环收文件时,收到5M就不再收,循环之后再recv就是md5了。
嗯,很好,具体怎么实现呢?
对客户端来说,只有最后一次可能粘包,EG:服务端传文件大小是50000KB,客户端收文件倒数直到第二次共收到49800,还剩下200,客户端最后一次还是收1024,这时若服务器(粘包)有发多余的数据就超了,就产生粘包了!
解决方法:客户端最后一次判断还剩多少未接收,直接收剩下的,不再收1024!
优化代码:
while receive_size < file_total_size:
if file_total_size - receive_size > 1024: #要收不止一次
size = 1024
else: #最后一次,剩多少收多少
size = file_total_size - receive_size
print("最后一次收:", size)
data = client.recv(size)
测试:
>>>:get tes
server file size 39
最后一次收: 39
file recv done
39 39
server file md5: b'c21eff88569c5ca26d8019bd891c27e9'
client file md5: c21eff88569c5ca26d8019bd891c27e9
>>>:
OK,无粘包,成功!
五、源码:
server:
import socket
import os
import hashlib server = socket.socket()
server.bind(("localhost", 9999)) server.listen(5) while True:
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()
if os.path.isfile(filename): #如果是文件
f = open(filename, "rb")
m = hashlib.md5() # 创建md5对象
file_size = os.stat(filename).st_size #获取文件大小
conn.send(str(file_size).encode()) #发送文件大小
conn.recv(1024) #接收客户端确认
for line in f:
conn.send(line) #发送数据
m.update(line)
print("file md5", m.hexdigest())
f.close()
conn.send(m.hexdigest().encode()) #发送md5 print(cmd,filename) server.close()
client:
import socket
import hashlib client = socket.socket() client.connect(("localhost", 9999)) while True:
cmd = input(">>>:").strip()
if len(cmd) == 0:
continue
if cmd.startswith("get"):
client.send(cmd.encode()) #客户端发送指令
receive_file_size = client.recv(1024)
print("server file size",receive_file_size.decode())
client.send("准备好接收文件了".encode()) #客户端发送确认 receive_size = 0
file_total_size = int(receive_file_size.decode())
filename = cmd.split()[1]
f = open(filename + ".new", "wb") #新文件,没有的话会创建
m = hashlib.md5() #生成md5对象 while receive_size < file_total_size: if file_total_size - receive_size > 1024: #要收不止一次
size = 1024
else: #最后一次,剩多少收多少
size = file_total_size - receive_size
print("最后一次收:", size)
data = client.recv(size) receive_size += len(data)
m.update(data)
f.write(data) #写到文件
else:
new_file_md5 = m.hexdigest() #根据收到文件生成的md5
print("file recv done")
print(receive_size, file_total_size)
f.close()
receive_file_md5 = client.recv(1024)
print("server file md5:", receive_file_md5)
print("client file md5:", new_file_md5) client.close()
[原创]python之socket-ftp的更多相关文章
- Python:socket实现ftp程序
刚开始学习socket编程,还不是特熟练,码了好长时间,中间遇到许多问题,记录一下用socketserver写ftp server端: #!/usr/bin/env python import soc ...
- Python 之socket的应用
本节主要讲解socket编程的有关知识点,顺便也会讲解一些其它的关联性知识: 一.概述(socket.socketserver): python对于socket编程,提供了两个模块,分别是socket ...
- 操作系统底层原理与Python中socket解读
目录 操作系统底层原理 网络通信原理 网络基础架构 局域网与交换机/网络常见术语 OSI七层协议 TCP/IP五层模型讲解 Python中Socket模块解读 TCP协议和UDP协议 操作系统底层原理 ...
- python之实现ftp上传下载代码(含错误处理)
# -*- coding: utf-8 -*- #python 27 #xiaodeng #python之实现ftp上传下载代码(含错误处理) #http://www.cnblogs.com/kait ...
- 【Python学习 】Python实现的FTP上传和下载功能
一.背景 最近公司的一些自动化操作需要使用Python来实现FTP的上传和下载功能.因此参考网上的例子,撸了一段代码来实现了该功能,下面做个记录. 二.ftplib介绍 Python中默认安装的ftp ...
- python通过socket实现多个连接并实现ssh功能详解
python通过socket实现多个连接并实现ssh功能详解 一.前言 上一篇中我们已经知道了客户端通过socket来连接服务端,进行了一次数据传输,那如何实现客户端多次发生数据?而服务端接受多个客户 ...
- python之socket编程(一)
socket之前我们先来熟悉回忆几个知识点. OSI七层模型 OSI(Open System Interconnection)参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标 ...
- 进击的Python【第十章】:Python的socket高级应用(多进程,协程与异步)
Python的socket高级应用(多进程,协程与异步)
- Python底层socket库
Python底层socket库将Unix关于网络通信的系统调用对象化处理,是底层函数的高级封装,socket()函数返回一个套接字,它的方法实现了各种套接字系统调用.read与write与Python ...
随机推荐
- IDDD 实现领域驱动设计-一个简单的 CQRS 示例
上一篇:<IDDD 实现领域驱动设计-CQRS(命令查询职责分离)和 EDA(事件驱动架构)> 学习架构知识,需要有一些功底和经验,要不然你会和我一样吃力,CQRS.EDA.ES.Saga ...
- 小菜学习设计模式(四)—原型(Prototype)模式
前言 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Method) ...
- geotrellis使用(八)矢量数据栅格化
目录 前言 栅格化处理 总结 参考链接 一.前言 首先前几天学习了一下Markdown,今天将博客园的编辑器改为Markdown,从编写博客到界面美观明显都清爽多了,也能写出各种样式的东 ...
- ZooKeeper集群搭建中的Connection refused而导致的启动失败
1. 前言 每一次搭建集群环境都像一次战斗,作战中任何一个细节的出错都会导致严重的后果,所以搭建中所需要做的配置如系统配置.网络配置(防火墙记得关).用户权限.文件权限还有配置文件等等内容,都必须非常 ...
- 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案
之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...
- H5基于iScroll实现下拉刷新,上拉加载更多
前言 前一段有个手机端的项目需要用到下拉刷新和上拉加载更多的效果,脑海里第一反映就是微博那种效果,刚开始的理解有些偏差,以为下拉也是追加数据,上拉也是追加数据,后请教同事后发现其实下拉只是刷新最新数据 ...
- 《C#并发编程经典实例》笔记
1.前言 2.开宗明义 3.开发原则和要点 (1)并发编程概述 (2)异步编程基础 (3)并行开发的基础 (4)测试技巧 (5)集合 (6)函数式OOP (7)同步 1.前言 最近趁着项目的一段平稳期 ...
- 实体之间的关系【Entity Relationships】(EF基础系列篇9)
Here, you will learn how entity framework manages the relationships between entities. Entity framewo ...
- 【转】JS编码解码、C#编码解码
GB2312,指的是中文 UTF8,指的是国标,包含中文.英文. 但是通过JQuery.ajax的Get.Post,如果直接传递中文或者特殊字符的特使字符的时候,这个时候就会出现乱码现象. JS编码 ...
- 关于log4net日志的配置流程
最近又重新整理一下log4net日志的配置,现在记录一下流程和一些遇到的问题,以备后续使用,具体的配置参数等信息.此文无,见谅! 1. 下载log4net.dll文件(网上很多,随便找一个!) 2. ...