• 〖Demo〗-- IO多路复用版FTP


    【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 = '
    %d%%' % (rate_num, ) #
     回到到开头
        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
  • 相关阅读:
    Python 之pymysql数据库模块
    Python 之sqlite3数据库模块
    Python 之操作sqlite3
    Python 之requests网络请求模块
    Python 之os文件目录模块
    Python 之json模块
    Python 之random随机数模块
    vue.js三种安装方式
    VUE学习之计算属性computed
    vue添加背景音乐
  • 原文地址:https://www.cnblogs.com/SHENGXIN/p/8097840.html
Copyright © 2020-2023  润新知