python 学习分享-实战篇高级的ftp

#server代码
import socketserver,os,hashlib

Base_paht = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/db'

class Server_ftp(socketserver.BaseRequestHandler):
    def handle(self):
        while 1:
            try:
                self.username = self.request.recv(1024).strip()          #接受用户信息
                if not self.username:continue
                self.username_path = Base_paht + '/%s/' % self.username.decode()   #获取用户文件地址
                print(self.username.decode(), '连接成功!')              #打印用户连接信息
                self.request.send('连接服务器成功!'.encode())
                while 1:
                    try:
                        self.data = self.request.recv(1024).strip()  #接受客户端命令
                        print('-->',self.data)
                        if not self.data :continue
                        print('{} wrote:'.format(self.client_address[0]),self.username)#打印ip及用户名称
                        cmd_dic = self.data.decode()   #命令编码
                        print('执行的命令为',cmd_dic)
                        if hasattr(self,cmd_dic + '_file'):   #判断是否存在这条命令
                            self.request.send('{}命令可以被执行'.format(cmd_dic).encode())
                            func = getattr(self,cmd_dic + '_file')    #反射
                            func()
                        else:
                            self.request.send('no'.encode())  #不存在发送错误信息
                    except ConnectionResetError as e:
                      #  print('err',e)
                        break
            except ConnectionResetError as e:
                print(self.client_address[0],e)
                break
    def get_file(self):
        filename = self.request.recv(1024).decode()
        print('客户端想要获取的文件名称为:',filename)
        if os.path.isfile(self.username_path + filename) :
            file_size = os.stat(self.username_path + filename).st_size  #确认文件大小
            self.request.send(str(file_size).encode())   #发送文件大小
            self.request.recv(1024)  #接收please give me
            f = open(self.username_path + filename,'rb')
            self.request.send(f.read())
            f.close()
            confirm = self.request.recv(1024).decode()
            print(confirm)
            f_1 = open(self.username_path + filename, 'rb')
            m2 = hashlib.md5()
            m2.update(f_1.read())
            f_1_m2 = m2.hexdigest()
            f.close()
            self.request.send(f_1_m2.encode())
            print(self.request.recv(1024).decode())


        else:
            self.request.send('no'.encode())   #如果文件不存在,发送no信息
    def put_file(self):
        filename = self.request.recv(1024).decode()
        if filename != 'no' :
            self.request.send('服务器收到文件名称!'.encode())
            file_size = self.request.recv(1024).decode()
            self.request.send('服务器收到文件大小为{}'.format(file_size).encode())
            f = open(self.username_path + filename,'wb')   #直接写入,如果有就覆盖
            confirm_size = 0
            while confirm_size < int(file_size):
                if int(file_size) - confirm_size > 1024:  # 确保接受的准确性,拒绝粘包.
                    cal = 1024
                else:
                    cal = int(file_size) - confirm_size
                data = self.request.recv(cal)
                confirm_size += len(data)
                f.write(data)
            f.close()
            f_1 = open(self.username_path + filename,'rb')
            m2 = hashlib.md5()
            m2.update(f_1.read())
            f_1_m2 = m2.hexdigest()
            f_1.close()
            self.request.send(f_1_m2.encode())
            print(self.request.recv(1024).decode())
            self.request.send('{}上传完成'.format(filename).encode())  # 返回服务器信息,表示下载完成

        else:
            print('客户端传送错误文件名称!')


    def open_file(self):
        filename = self.request.recv(1024).decode()
        self.request.send('服务器收到目录名称!'.encode())
        self.request.recv(1024)  #防止粘包
        if os.path.exists(self.username_path + filename ):
            self.request.send('yes'.encode())
            self.request.recv(1024)
            path_dir = os.listdir(self.username_path+ filename)
            if path_dir == []:  # 如果为空
                self.request.send('nothing'.encode())
            else:
                self.request.send(str(path_dir).encode())
        else:
            self.request.send('no'.encode())

    def look_file(self):
        self.request.recv(1024)        #接收返回的数据,防止粘包
        path_dir = os.listdir(self.username_path)
        if path_dir == []:                       #如果为空
            self.request.send('nothing'.encode())
        else:
            self.request.send(str(path_dir).encode())





if __name__ =='__main__':
    HOST,PORT = 'localhost' , 6969
    server = socketserver.ThreadingTCPServer((HOST,PORT),Server_ftp)
    print('----------->等待连接<------------')
    server.serve_forever()
#client代码


import socket,os,pickle,hashlib,sys



floder_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/user_floder'
user_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/conf'
server_db_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + '/server_ftp/db/'


class Scoket_ftp(object):
    # 客户端类
    def __init__(self):
        self.client = socket.socket()

    def client_user_register(self):
        #用户注册
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        if not os.path.exists(user_path + '/%s'%username) :
            pwd_md = hashlib.md5()       #md5加密用户密码
            pwd_md.update(bytes(password,encoding='utf-8'))
            pwd_save = pwd_md.hexdigest()
            pickle.dump({'username':username,'password':pwd_save},open(user_path + '/%s'%username,'wb'))
            os.makedirs(server_db_path + username)
            print('创建成功!')
        else:
            print('账户已经存在!')


    def client_user_login(self):
        #身份验证
        username = input('请输入用户名:').strip()
        password = input('请输入密码:').strip()
        if os.path.exists(user_path + '/%s'%username) :
            pwd_md = hashlib.md5()  # md5加密用户密码
            pwd_md.update(bytes(password,encoding='utf-8'))
            pwd_save = pwd_md.hexdigest()
            user_dic = pickle.load(open(user_path + '/%s'%username,'rb'))
            if username == user_dic['username'] and pwd_save == user_dic['password'] :
                print('%s登录成功'%username)
                return username
            else:
                print('账户密码错误!')
                return False
        else:
            print('账户密码错误!')
            return False

    def client_conn(self,ip_addr,port):
        #建立连接
        while 1:
            username = self.client_user_login()
            if username :
                self.client.connect((ip_addr, port))
                self.client.send(username.encode())
                print(self.client.recv(1024).decode())
                break
            else:
                continue


    def help(self):
        #帮助信息
        print('''
        -----------help-----------
        look:查看当前目录下文件
        open foldername:打开文件夹
        get filename:下载文件
        put filename:上传文件
                ''')
    def client_interaction(self):
        #交互
        while 1:
            self.help()
            cmd = input('请输入操作命令:').strip()
            if len(cmd.split()) == 1:  #判断命令行长度,如果是1的话,在判断输入
                if cmd == 'look':

                    self.file_look()#使用look方法
                elif cmd == 'exit':
                    self.file_exit()
                else:
                    print('命令输入错误,请重新输入')
                    continue
            elif len(cmd.split()) == 2: #长度为2,证明是带有前面的语句的
                cmd_option, filename = cmd.split()
                if hasattr(self,'file_' + cmd_option):
                    func = getattr(self,'file_' +cmd_option)
                    func(filename)
                else:
                    continue
            else:
                continue
    def file_look(self):
        #查看
        self.client.send('look'.encode()) #发送方法
        print(self.client.recv(1024).decode())  # 服务器确认信息,后面可以加入判断,判断服务器目前状态是否正常
        self.client.send('please give me!'.encode())  # 自动发送给服务器
        print('文件目录:',self.client.recv(1024).decode()) #接收目录信息并打印
    def file_get(self,filename):
        #下载
        self.client.send('get'.encode())  #先发送方法
        print(self.client.recv(1024).decode())  #服务器确认信息,后面可以加入判断,判断服务器目前状态是否正常
        self.client.send(filename.encode())  #再发送文件名称
        confirm = self.client.recv(1024).decode()  #服务器确认文件是否存在,如果存在返回文件大小,如果不存在,返回no
        if confirm != 'no':
            self.client.send('please give me!'.encode())
            f = open(floder_path+'/'+filename,'wb')  #在下载文件夹中创建该文件(如果存在,则替换)
            confirm_cal = 0
            rate = 1
            while confirm_cal < int(confirm):
                if int(confirm) - confirm_cal > 1024:   #确保接受的准确性,拒绝粘包.
                    cal = 1024
                else:
                    cal = int(confirm) - confirm_cal
                data = self.client.recv(cal)
                confirm_cal += len(data)   #避免tcp拆包
                f.write(data)
                if int(confirm) > 102400:
                    if confirm_cal  > int(confirm)/100*(rate+1) and rate<= 100  :
                        rate += 1
                        r = '\r[%s]%d%%' % ("=" * rate, rate)
                        sys.stdout.write(r)
                        sys.stdout.flush()
                    else:
                        continue
                else:
                    r = '[%s]%d%%'%('='*100,100)
                    sys.stdout.write(r)
            f.close()
            self.client.send('{}下载完成'.format(filename).encode())  # 返回服务器信息,表示下载完成
            f_1 = open(floder_path+'/'+filename,'rb')  #判断一致性
            m2 = hashlib.md5()
            m2.update(f_1.read())
            f_1_m2 = m2.hexdigest()
            f_1.close()
            f_1_m2_server = self.client.recv(1024).decode()
            if f_1_m2 == f_1_m2_server :
                print('\n下载完成!')
                self.client.send('客户端已经成功获取完整文件!'.encode())
            else:
                print('传输异常')
                self.client.send('客户端获取文件不完整或存在异常!'.encode())
        else:
            print('确认信息为no,文件可能不存在或有其他问题!')

    def file_put(self,filename):
        #上传文件
        self.client.send('put'.encode())  # 先发送方法
        print(self.client.recv(1024).decode())  # 服务器确认信息,后面可以加入判断,判断服务器目前状态是否正常
        if os.path.isfile(floder_path + '/' + filename):
            self.client.send(filename.encode())
            print(self.client.recv(1024).decode())  #拒绝粘包
            cal = os.stat(floder_path + '/' + filename).st_size #获取文件大小
            self.client.send(str(cal).encode()) #发送文件大小信息
            print(self.client.recv(1024).decode())  #获取反馈
            f = open(floder_path+'/'+filename,'rb')  #打开文件
            self.client.send(f.read())
            f.close()
            f_1 = open(floder_path + '/' + filename, 'rb')  #一致性校验
            m2 = hashlib.md5()
            m2.update(f_1.read())
            f_1_m2 = m2.hexdigest()
            f_1.close()
            f_1_m2_server = self.client.recv(1024).decode()
            if f_1_m2 == f_1_m2_server:
                print('上传完成!')
                self.client.send('客户端已经成功上传完整文件!'.encode())
            else:
                print('传输异常')
                self.client.send('客户端上传文件不完整或存在异常!'.encode())
                print(self.client.recv(1024).decode())
        else:
            self.client.send('no'.encode())
            print('查无此文件')
    def file_open(self,filename):
        self.client.send('open'.encode())  # 先发送方法
        print(self.client.recv(1024).decode())  # 服务器确认信息,后面可以加入判断,判断服务器目前状态是否正常
        self.client.send(filename.encode()) #发送目录名称
        print(self.client.recv(1024).decode())
        self.client.send('防止粘包'.encode())
        confirm = self.client.recv(1024).decode()  # 服务器确认文件是否存在,如果存在返回yes,如果不存在,返回no
        if confirm != 'no':
            self.client.send('please give me!'.encode())  #自动发送给服务器
            file_dir = self.client.recv(1024).decode()
            print(file_dir)

        else:
            print('确认信息为no,目录可能不存在或有其他问题!')


if __name__ == '__main__' :
    socket_ftp = Scoket_ftp()
    option = input('''
    ---------option----------
    1.创建用户
    2.登录
    3.退出
    ''').strip()
    if option == '1':
        socket_ftp.client_user_register()
    elif option == '2':
        socket_ftp.client_conn('localhost',6969)

        socket_ftp.client_interaction()
    elif option == '3':
        exit()

    else:
        print('wrong!')
        exit()

 

posted @ 2017-08-17 11:02  laay  阅读(210)  评论(0编辑  收藏  举报