[原创]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 ...
随机推荐
- JavaWeb路径问题打包总结--小心出门右转404
话说,培训和自学就不是一个回事,两周讲完java基础,两天讲完jsp,两节课讲完servlet,还真不是一般人能受得了的,这两天学习jsp和servlet频繁被路径问题困扰,倒不是出错,只是各种act ...
- 【JUC】JDK1.8源码分析之CopyOnWriteArraySet(七)
一.前言 分析完了CopyOnWriteArrayList后,下面接着分析CopyOnWriteArraySet,CopyOnWriteArraySet与CopyOnWriteArrayList有莫大 ...
- MVC前台Post/Get异步获得数据时参数的取值问题
Post方法,返回text,后台获得Data View $.ajax({ type: "POST", dataType: "text",//返回类型为文本 ur ...
- ODBC database driver for Go:Go语言通过ODBC 访问SQL server
Go语言通过ODBC 访问SQL server,这里需要用到go-odbc库,开源地址::https://github.com/weigj/go-odbc 一.驱动安装 在cmd中打开GOPATH: ...
- iOS的一些面试题分析总结(0)
虽然一些东西在实际工作中我们是很少用到的,但是面试确实会经常问到一些我们不常用的东西,所以说有时候看一看还是有必要的,一方面面试也是很重要的一件事,另一方面某些情况下也能帮我们查漏补缺. 一.NSNo ...
- Hexo折腾记
如果时间可以静止,我希望就停在此刻. 前言 博主之前也有折腾wordpress和jekyll可对于一个前端er来说,wordpress让人没法尽兴,因为不知道该如何添加自己的代码.而jekyll就太麻 ...
- 8.2 使用Fluent API进行实体映射【Code-First系列】
现在,我们来学习怎么使用Fluent API来配置实体. 一.配置默认的数据表Schema Student实体 using System; using System.Collections.Gener ...
- Xamarin.Android之简单的抽屉布局
0x01 前言 相信对于用过Android版QQ的,应该都不会陌生它那个向右滑动的菜单(虽说我用的是Lumia) 今天就用Xamarin.Android实现个比较简单的抽屉布局.下面直接进正题. 0x ...
- 【C语言学习趣事】_33_关于C语言和C++语言中的取余数(求模)的计算_有符号和无符号数的相互转换问题
最近再次复习C++语言,用的教材是<C++ Primer>这本教材, 看到第二章的时候,里面有个问题困扰了我. 于是想上网查查怎么回事, 结果看了很久都没有得到一个满意的答案. 书上有这么 ...
- No.023:Merge k Sorted Lists
问题: Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexit ...