使用IO多路复用实现FTP上传下载
服务端
import time,os,selectors
from socket import *
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Server:
def __init__(self):
self.ip_port = ("127.0.0.1",8001)
self.sel = selectors.DefaultSelector()#根据平台选择最佳的IO多路机制,比如linux就会选择epoll
self.dic = {}
self.create_sock()
self.handle()
def create_sock(self):
sk = socket(AF_INET,SOCK_STREAM)
sk.bind((self.ip_port))
sk.setblocking(False)
sk.listen(5)
self.sel.register(sk,selectors.EVENT_READ,self.accept)#注册监听对象
def handle(self):
while True:
events = self.sel.select() ##监听谁发生变化了
for key,mask in events:
func = key.data #获取对象调用的函数
func(key.fileobj,mask)
def accept(self,sock,mask):
print("登录服务端")
conn ,addr = sock.accept()
#data = conn.recv(1024)
self.sel.register(conn,selectors.EVENT_READ,self.read)
self.dic[conn] = {}
def read(self,conn,mask):
try:
if not self.dic[conn]:
data = conn.recv(1024)
try:
cmd ,filename ,filesize =str(data.decode("utf-8")).split("|")
self.dic[conn] = {"cmd": cmd, "filename": filename, "filesize": filesize, "hasReceived": 0}
except Exception as e:
cmd, filename= str(data.decode("utf-8")).split("|")
self.dic[conn] = {"cmd": cmd, "filename": filename,"filesize":os.path.getsize(os.path.join(BASE_DIR,"load",filename)),
"hasSended": 0}
if cmd == "put": #上传
conn.send("ok".encode("utf-8"))
if cmd =="get":#下载
conn.send(str(self.dic[conn]["filesize"]).encode("utf-8"))
else:
if self.dic[conn].get("cmd",None):
cmd = self.dic[conn].get("cmd")
if hasattr(self,cmd):
func = getattr(self,cmd)
func(conn,mask)
else:
print("error")
except Exception as e:
print(e)
print(conn,"断开连接")
conn.close()
self.sel.unregister(conn)
def put(self,conn,mask):
filename = self.dic[conn]['filename']
filesize = self.dic[conn]['filesize']
path = os.path.join(BASE_DIR, "upload", filename)
data = conn.recv(1024)
conn.send("success".encode("utf-8"))
self.dic[conn]['hasReceived']+=len(data)
with open(path,"ab") as f:
f.write(data)
if self.dic[conn]['hasReceived'] == self.dic[conn]['filesize']:
self.dic[conn] = {}
print("上传结束")
def get(self,conn,mask):
filename = self.dic[conn]['filename']
path = os.path.join(BASE_DIR,"load",filename)
with open(path, "rb") as f:
f.seek(self.dic[conn]["hasSended"],0)
data = f.read(10)
conn.send(data)
self.dic[conn]['hasSended']+=len(data)
if self.dic[conn]['hasSended'] ==self.dic[conn]["filesize"]:
self.dic[conn]={}
print("下载结束")
time.sleep(0.5)
if __name__ == "__main__":
Server()
客户端
import os,time,sys
BASE_DIR =os.path.dirname(os.path.abspath(__file__))
import socket
import selectors
class selectFtpClient:
def __init__(self):
self.port = ("127.0.0.1",8001)
self.create_socket()
self.command_fanout()
def create_socket(self):
try:
self.sock = socket.socket()
self.sock.connect(self.port)
print("连接FTP成功!")
except Exception :
print("连接失败!")
def command_fanout(self):
while True:
cmd = input(">>>").strip()
if cmd == "exit()":
break
cmd,file=cmd.split()
if hasattr(self,cmd):
func = getattr(self,cmd)
func(cmd,file)
else:
print('调用错误!')
#上传
def put(self,cmd,file):
if os.path.isfile(file):
fileName = os.path.basename(file)
fileSize = os.path.getsize(file)
fileInfo = "%s|%s|%s"%(cmd,fileName,fileSize)
self.sock.send(bytes(fileInfo,encoding="utf-8"))
recvStatus = self.sock.recv(1024)
hasSend = 0
if str(recvStatus,encoding="utf-8") =='ok':
with open(file,"rb") as f:
while fileSize >hasSend:
contant = f.read(10)
recv_size = len(contant)
self.sock.send(contant)
hasSend+=recv_size
s = str(int(hasSend/fileSize*100))+"%"
print("正在上传文件:"+fileName+" 已经上传: "+s)
time.sleep(0.5)
print("%s文件上传完毕"%(fileName,))
else:
print("文件不存在")
#下载
def get(self,cmd,file):
fileInfo = "%s|%s"%(cmd,file)
self.sock.send(bytes(fileInfo,encoding="utf-8"))
filesize = int(self.sock.recv(10).decode("utf-8"))
f = open(file,"ab")
i=0
while i<filesize:
self.sock.send("ok".encode("utf-8"))
data = self.sock.recv(10)
f.write(data)
i+=len(data)
s = str(int(i / filesize * 100)) + "%"
print("正在上传文件:" + file + " 已经下载: " + s)
print("下载完成")
f.close()
if __name__ == "__main__":
selectFtpClient()