利用IO多路复用实现的ftp的上传与下载

时间:2022-07-01 17:54:47

程序的基本框架图

利用IO多路复用实现的ftp的上传与下载

这个代码我写的时候偷了点懒,没有写关于data的内容  我将需要下载的文件和需要上传的文件都各自放在其client或者server下

其实这个代码很简单,只要理解了IO多路复用,get与put其实两个可逆的过程,注意 发送 与 接受 的位置顺序就可以了

我是新手 希望大家指教

代码 客户端 :

import socket
import os
import hashlib
import time
#client的登陆页面我后续在写
class myclient(object):
def __init__(self):
pass
def login(self):
self.client
=socket.socket()
#我测试的时候应该往linux上服务器端传输文件
# 手动输入账户与端口号码
self.client.connect(("localhost",23))
def interactive(self):#设计一个用来交互的函数可用来输入命令
while True:
command
=input("请输入命令").strip()#命令模式参考read me
if not command:
continue
else:
cmd
=command.split()[0]
if hasattr(self,cmd):
func
=getattr(self,cmd)
func(command)
#通过这个函数 将下面的dowload与put链接在了一起
#下载文件
def get(self,command):
#把文件名字发过去看是否可以执行(其实就是发过去看服务器端到底有没有这个文件可以下载)
self.client.sendall(command.encode())
#根据返回的状态码我们来判断是否该继续进行
filename=command.split()[1]
status_code
=self.client.recv(1024).decode()
if status_code=="203":
print("我们可以进行下载".center(50,"-"))
#接下来我们要继续判断是否已经下载了一部分,1.继续下载 2.从零开始下载
if os.path.isfile(filename):#判断文件是否已经存在过
self.client.sendall("000".encode())#发送一个 000命令告诉服务器 这个文件在客户端已经下载过
recive_size=os.stat(filename).st_size#已经接受的尺寸
self.client.sendall(str(recive_size).encode())#将尺寸发送给服务器端
status_code=self.client.recv(1024).decode()
if status_code=="426":#文件曾经下载过
print("我们需要接着继续传输")
elif status_code=="312":
print("文件已经下载完毕不需要在下载了")
else:
self.client.sendall(
"402".encode())
print("开始传输")
recive_size
=0
filesize
=self.client.recv(1024).decode()
print(filesize)#所需要下载的文件的尺寸
filesize=int(filesize)
m
=hashlib.md5()
with open(filename,
"ab") as f:
filesize
+=recive_size
print("源文件的总大小为",filesize)
print("已接受文件大小为",recive_size)
while recive_size < filesize:
a
=filesize-recive_size
print(a)
if a>1024:
size
=1024
else:
size
=a
data
=self.client.recv(size)
recive_size
+=len(data)
f.write(data)
m.update(data)

new_hexdigest
=m.hexdigest()
server_hexdigest
=self.client.recv(1024).decode()
if new_hexdigest==server_hexdigest:
print("文件一致")


else:
print("文件在服务器端不存在!")

#上传文件将client端的文件传送给server端
def put(self,command):
self.client.sendall(command.encode())
filename
=command.split()[1]#客户端发来的判断这里是否有这个文件
if os.path.isfile(filename):
self.client.sendall(
"203".encode())#有这个文件可以上传
file_allsize=os.stat(filename).st_size #获得本地文件的大小
while True:
try:
status_code
=self.client.recv(1024).decode()#获取状态码后继续执行
break
except:
continue
if status_code=="402":
print("服务器端未下载过,从头开始发送")
file_hasrecivesize
=0
self.client.sendall(str(file_allsize).encode())
elif status_code=="000":#此状态码说明客户端已经下载过
file_hasrecivesize=self.client.recv(1024).decode()
file_hasrecivesize
=int(file_hasrecivesize)
if file_hasrecivesize<file_allsize:#如果已接受文件尺寸小于总尺寸那么传一个继续下载信号
self.client.sendall("426".encode())
elif file_hasrecivesize==file_allsize:
self.client.sendall(
"312".encode())
print("文件已经下载过无需继续进行下载")

shouldsendsize
=file_allsize-file_hasrecivesize
self.client.sendall(str(shouldsendsize).encode())

m
=hashlib.md5()
with open(filename,
"rb") as file:
file.seek(file_hasrecivesize)
for line in file:
m.update(line)
self.client.sendall(line)
self.client.sendall(m.hexdigest().encode())
print(self.client.recv(1024).decode())



else:
print("服务器端无此文件可供下载")
self.client.sendall(
"110".encode())
#之后我们写的是命令行的操作指示传递



d
=myclient()
d.login()
d.interactive()

 

代码 服务器端:

 

import hashlib
import socket
import os
import selectors
global command
global conn
import time
sel
=selectors.DefaultSelector()
#创建一个 sel实例

def accept(sock,mask):
sock
=sock
mask
=mask
conn,addr
=sock.accept()
conn.setblocking(False)
sel.register(conn,selectors.EVENT_READ,read)
def read(conn, mask):
data
= conn.recv(1000) # 获得命令
if data:
print(data)
command_str
= data.decode()
print(command_str)
if command_str.split()[0] == 'get':
get(conn, command_str)
elif command_str.split()[0]=="put":
put(conn,command_str)

else:
conn.send(b
'404') # Hope it won't block
else:
print('closing', conn)
sel.unregister(conn)
conn.close()
#与客户端的下载进行交互
def get(conn,command):
filename
=command.split()[1]#客户端发来的判断这里是否有这个文件
if os.path.isfile(filename):
conn.sendall(
"203".encode())#有这个文件可以下载
file_allsize=os.stat(filename).st_size #获得本地文件的大小
while True:
try:
status_code
=conn.recv(1024).decode()#获取状态码后继续执行
break
except:
continue
if status_code=="402":
print("客户端未下载过,从头开始发送")
file_hasrecivesize
=0
conn.sendall(str(file_allsize).encode())
elif status_code=="000":#此状态码说明客户端已经下载过
while True:
try:
file_hasrecivesize
=conn.recv(1024).decode()#获取状态码后继续执行
break
except:
continue
file_hasrecivesize
=int(file_hasrecivesize)
print(file_hasrecivesize)
if file_hasrecivesize < file_allsize:#如果已接受文件尺寸小于总尺寸那么传一个继续下载信号
conn.sendall("426".encode())
print("文件小于总尺寸")
elif file_hasrecivesize == file_allsize:
conn.sendall(
"312".encode())
print("文件等于总尺寸")
shouldsendsize
=file_allsize-file_hasrecivesize
conn.sendall(str(shouldsendsize).encode())

m
=hashlib.md5()
with open(filename,
"rb") as file:
file.seek(file_hasrecivesize)
for line in file:
m.update(line)
conn.sendall(line)
conn.sendall(m.hexdigest().encode())
return

else:
print("服务器端无此文件可供下载")
conn.sendall(
"110".encode())

def put(conn,command):
filename
=command.split()[1]
while True:
try:
status_code
=conn.recv(1024).decode()#获取状态码后继续执行
break
except:
continue
if status_code=="203":
print("我们可以进行下载".center(50,"-"))
#接下来我们要继续判断是否已经下载了一部分,1.继续下载 2.从零开始下载
if os.path.isfile(filename):#判断文件是否已经存在过
conn.sendall("000".encode())#发送一个 000命令告诉服务器 这个文件在客户端已经下载过
recive_size=os.stat(filename).st_size#已经接受的尺寸
conn.sendall(str(recive_size).encode())#将尺寸发送给服务器端
while True:
try:
status_code
=conn.recv(1024).decode()#获取状态码后继续执行
break
except:
continue
if status_code=="426":#文件曾经下载过
print("我们需要接着继续传输")
elif status_code=="312":
print("文件已经下载完毕不需要在下载了")
else:
conn.sendall(
"402".encode())
print("开始传输")
recive_size
=0
while True:
try:
filesize
=conn.recv(1024).decode()#所需要下载的文件的尺寸
break
except:
continue

filesize
=int(filesize)
print(filesize)
m
=hashlib.md5()
with open(filename,
"ab") as f:
filesize
+=recive_size
print("源文件的总大小为",filesize)
print("已接受文件大小为",recive_size)
while recive_size < filesize:
a
=filesize-recive_size
print(a)
if a>1024:
size
=1024
else:
size
=a
data
=conn.recv(size)
recive_size
+=len(data)
f.write(data)
m.update(data)
new_hexdigest
=m.hexdigest()
while True:
try:
server_hexdigest
=conn.recv(1024).decode()
break
except:
continue

if new_hexdigest==server_hexdigest:
print("文件一致")
conn.send(
"文件一致".encode())
else:
print("文件在服务器端不存在!")



server
=socket.socket()#创建一个实例
#
监听的链接 我选择从客户端接收
server.bind(("localhost",23))
server.listen(
1000)

server
=socket.socket()
server.bind((
"localhost",23))
server.listen(
1000)
server.setblocking(False)
#设置为非阻塞模式
sel.register(server,selectors.EVENT_READ,accept)
while True:
events
=sel.select()
for key,mask in events:
callback
=key.data#相当于callback=accept
callback(key.fileobj,mask)#调用accept keyfileobj相当于一个实例作为形参传进去

 

其实这个程序要理解 多路复用的原理,在服务器端 服务器一般是不会等待接受的 如果100个链接过来都没有那么就会立即返回一个信号结束,所以我们要用一个while循环来等recv信息继续进行

才能正确的运行代码!!!!