IO多路复用版FTP

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IO多路复用版FTP相关的知识,希望对你有一定的参考价值。

需求:

  1. 实现文件上传及下载功能
  2. 支持多连接并发传文件
  3. 使用select or selectors

流程图

技术分享

技术分享

技术分享
import socket
import pickle
import sys
import time
import os

A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class Ftp_client(object):
    def __init__(self):
        self.client = socket.socket()
    def connet(self, ip, port):
        ‘‘‘
        链接服务器
        :param ip:
        :param port:
        :return:
        ‘‘‘
        self.client.connect((ip, port))
        name = self.main()
        self.ftp_main(name)
    def login(self):
        ‘‘‘
        登录
        :return:
        ‘‘‘
        name = input(请输入姓名).lower().strip()
        password = input(请输入密码)
        dict = {
            attr: login,
            name: name,
            password: password,
        }
        self.client.sendall(pickle.dumps(dict))
        data = self.client.recv(1024)
        print(data.decode())
        if data.decode()==输入有误:
            return False
        else:
            return name

    def register(self):
        ‘‘‘
        注册
        :return:
        ‘‘‘
        name = input(请输入姓名).lower().strip()
        pd = input(请输入密码)
        dict = {attr: register, name: name, password: pd}
        self.client.sendall(pickle.dumps(dict))
        data = self.client.recv(1024)
        print(data.decode())
        if data.decode() == 用户名已存在,请重新输入:
            return False
        else:
            return name

    def main(self):
        while True:
            a = input(请输入 1. 用户登录 2. 用户注册 3.退出)
            if a == 1:
                res = self.login()
            elif a == 2:
                res = self.register()
            elif a == 3:
                exit()
            else:
                print(输入有误)
                continue
            if res is False:
                    continue
            else:
                return res

    def download(self, name):
        ‘‘‘
        下载
        :return:
        ‘‘‘
        filename = input(请输入下载文件名)
        dic = {attr: download, filename: filename, name: name}
        self.client.sendall(pickle.dumps(dic))
        size = self.client.recv(1024).decode()
        if size == 该文件不存在:
            print (该文件不存在)
            return False
        else:
            size = int(size)
            try:
                f = open(os.path.join(A, client,db, file, filename), xb) #文件不存在新建
            except Exception:
                f = open(os.path.join(A, client,db, file, filename), wb)#文件存在打开重新下载
            if size == 0:
                f.close()
                print(接收完成)
            else:
                r_size = 0
                while r_size < size:
                    file = self.client.recv(1024)
                    f.write(file)
                    r_size += len(file)
                    view_bar(r_size, size)
                    time.sleep(0.1)
                else:
                    print(接收完成)
                    f.close()



    def upload(self, name):
        filename = input(请输入上传的文件名)
        if os.path.exists(os.path.join(A, client, db, file,filename)) and filename !=.:
            size = os.path.getsize(os.path.join(A, client, db, file, filename)) #文件size
            f = open(os.path.join(A, client, db, file, filename), rb)
        else:
            print (此文件不存在)
            return False
        if size == 0:
            dic = {attr: upload, filename: filename, size: size, name: name}
            self.client.sendall(pickle.dumps(dic))
        else:
            for line in f:
                dic = {attr: upload, filename: filename, size: size, name: name, text: line}
                self.client.sendall(pickle.dumps(dic))
                num = f.tell() #查看文件上传位置
                view_bar(num, size)
                time.sleep(0.1)
        f.close()
        print (接收完成)
        return False




    def ftp_main(self, name):
        while True:
            a = input(请输入相应的指令, download: 下载; upload: 上传; exit:退出).strip()
            if hasattr(self, a):
                func = getattr(self, a)
                func(name)
            elif a == exit:
                exit()
            else:
                print(输入有误)






def view_bar(num, total):
    ‘‘‘进度条‘‘‘
    rate = float(num) / float(total)
    rate_num = int(rate * 100)
    r = \\r%d%% % (rate_num, ) #\\r 回到到开头
    sys.stdout.write(r)
    sys.stdout.flush()  #删除记录

ftp = Ftp_client()
ftp.connet(localhost, 9999)
client
技术分享
import selectors
import socket
import pickle
import os
A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sel = selectors.DefaultSelector()



def accept(sock,mask):
    conn, addr = sock.accept()
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read) #调用read函数


def read(conn, mask):
    try:
        data = conn.recv(1024)
    except Exception:
        sel.unregister(conn) #出现异常,取消注册的conn
        conn.close()
    else:
        if data:
            r_dic = pickle.loads(data)
            if r_dic[attr] == login:
                login(conn, r_dic)
            elif r_dic[attr] == register:
                register(conn, r_dic)
            elif r_dic[attr] == download:
                download(conn, r_dic)
            elif r_dic[attr] == upload:

                upload(r_dic)
        else:
            print(链接断开)
            sel.unregister(conn)
            conn.close()





def login(conn, dict):
    ‘‘‘
    登录
    :param conn:
    :param dict:
    :return:
    ‘‘‘
    db_dict = pickle.load(open(os.path.join(A, server, db, register),rb))
    if dict[name] in db_dict:
        if dict[password]==db_dict[dict[name]][0]:
            conn.sendall(登录成功.encode())
        else:
            conn.sendall(输入有误.encode())
    else:
        conn.sendall(输入有误.encode())


def register(conn, r_dic):
    ‘‘‘
    注册
    :param conn:
    :param r_dic:
    :return:
    ‘‘‘
    if os.path.exists(os.path.join(A, db, r_dic[name])):
        conn.sendall(用户名已存在,请重新输入.encode())
    else:
        conn.sendall(注册成功.encode())
        os.makedirs(os.path.join(A, server, db, r_dic[name]))
        n_dict = pickle.load(open(os.path.join(A, server, db, register), rb))
        n_dict[r_dic[name]] = r_dic[password]
        pickle.dump(n_dict,open(os.path.join(A, server,db, register), wb))


def upload(dic):
    ‘‘‘
    下载
    :param dic:
    :return:
    ‘‘‘
    print(dic)
    f_size = int(dic[size]) #上传文件大小
    print(f_size)
    filename = dic[filename]
    name = dic[name]
    try:
        f = open(os.path.join(A, server,db, name, filename), xb)
        file_size = 0
        print(f_size)
    except Exception:
        f = open(os.path.join(A, server,db, name, filename), ab)
        file_size = os.path.getsize(os.path.join(A, server, db, name, filename))
    if f_size == 0:
        f.close()
        return False
    else:
        if file_size< f_size:
            f.write(dic[text])
            f.flush()
        else:
            f.close()
            return False

def download(conn, dic):
    ‘‘‘
    下载
    :param conn:
    :param dic:
    :return:
    ‘‘‘
    l_path = os.path.join(A, server,db, dic[name], dic[filename])
    if os.path.exists(l_path) and dic[filename]!= .: #检查文件是否存在,文件名不能等于.
        f = open(l_path, rb)
        size = os.path.getsize(l_path) #检查文件
        conn.sendall(str(size).encode()) #要以字符串格式传数字
        for line in f:
            conn.sendall(line)
        f.close()
    else:
        conn.sendall(该文件不存在.encode())



sock = socket.socket()
sock.bind((localhost, 9999))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
    events = sel.select()
    for key, mask in events:
        callback = key.data
        callback(key.fileobj, mask)
server

 

以上是关于IO多路复用版FTP的主要内容,如果未能解决你的问题,请参考以下文章

python协程和异步IO——IO多路复用

请不要再说NIO和多路复用IO是同一个东西了(内含BIONIO多路复用NettyAIO案例测试代码)

Python IO多路复用

你管这破玩意叫 IO 多路复用?

多路转接(IO复用)接口介绍

多路复用io接口-epoll