Python socket编程-进阶5

基于socket搭建的 FTP文件上传功能模块

  1.  用户账号密码验证功能

  2.  断点续传功能

  3. 文件目录切换功能

  4. MD5加密功能

 

使用方法

  1.  服务端Terminal 输入 python ftp_server.py  start 启动服务端

  2. 客户端使用 Terminal 输入 python ftp_client.py -s localhost -p 8081 -u liang -w 123

  3. 客户端输入 put 123.png images   ( put 为上传方法, 123.png 为客户端下的文件, images为上传到服务端指定的文件夹 )

 

项目结构

  

 

FTP_client 

import socket
import optparse
import json,os,sys


class ClientHandler:

    def __init__(self):
        """获取 Terminal 指令参数"""
        self.op = optparse.OptionParser()
        self.op.add_option('-s', '--server', dest='server')
        self.op.add_option('-p', '--port', dest='port')
        self.op.add_option('-u', '--username', dest='username')
        self.op.add_option('-w', '--password', dest='password')
        self.options, self.args = self.op.parse_args()
        self.mainpath=os.path.dirname(os.path.abspath(__file__))
        self.verify_args()
        self.make_connection()

    def verify_args(self):
        """解析从 Terminal 获取的指令参数"""
        server = self.options.server
        port = int(self.options.port)
        username = self.options.username
        password = self.options.password

        if 0 < port <= 65535:
            return True
        else:
            exit('端口号必须介于 0~65535之间')

    def make_connection(self):
        """获取socket连接"""
        self.tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp.connect((self.options.server, int(self.options.port),))

    def interactive(self):
        """通讯"""
        flag=self.auth()
        if flag:
            while True:
                cmdinfo=input('%s >>>'%self.curdir).strip()
                cmdlist=cmdinfo.split()
                if cmdlist[0]:
                    if hasattr(self,cmdlist[0]):
                        func=getattr(self,cmdlist[0])
                        func(cmdlist)
                    else:
                        print('指令不正确')
                else:
                    pass


    def auth(self):
        """验证"""
        username = self.options.username
        password = self.options.password
        if username is None or password is None:
            username = input('username:')
            password = input('password:')

        data = {
            'action': 'auth',
            'username': username,
            'password': password,
        }

        self.tcp.send(json.dumps(data).encode('GBK'))
        data = self.response()

        if data['state_code'] ==200:
            print(data)
            self.user=username
            self.curdir=username
            return True
        else:
            print(data)
            return False


    def put(self,cmdlist):
        action,localpath,targetpath=cmdlist
        localpath=os.path.join(self.mainpath,localpath)
        filename=os.path.basename(localpath)
        filesize=os.stat(localpath).st_size
        data={
            'action':action,
            'targetpath':targetpath,
            'filename':filename,
            'filesize':filesize
        }
        self.tcp.sendall(json.dumps(data).encode('utf-8'))

        data=self.response()
        if data['state_code'] ==800: # 文件不存在,可以上传!
            self.read(localpath,filesize,'rb')

        elif data['state_code'] ==801: # 断点续传
            choices=input('do you want continue? [Y/N]:')
            if choices.upper()=='Y':
                self.tcp.sendall('Y'.encode('utf-8'))
                position=self.tcp.recv(1024).decode('utf-8')
                has_send=int(position)
                self.read(localpath,filesize,'rb',has_send=has_send)
            elif choices.upper() =='N':
                self.tcp.sendall('N'.encode('utf-8'))

        elif data['state_code'] ==802: #文件已经存在
            print(data)
            return


    def read(self,filename,filesize,mode,has_send=0):
        f=open(filename,mode)
        f.seek(has_send)
        while has_send<filesize:
            data=f.read(200)
            self.tcp.sendall(data)
            has_send+=len(data)
            self.show_progress(has_send,filesize)
        f.close()
        print('upload finish')

    def show_progress(self,has_send,filesize):
        rate=float(has_send)/float(filesize)
        rate=int(rate*100)
        sys.stdout.write('%s%%  %s\r'%(rate,'*'*rate))


    def ls(self,cmdlist):
        action=cmdlist[0]
        data = {
            'action': action,
        }
        self.tcp.sendall(json.dumps(data).encode('utf-8'))

        data = self.tcp.recv(1024).decode('utf-8')
        print(data)


    def cd(self,cmdlist):
        action,dirname=cmdlist
        data = {
            'action': action,
            'dirname':dirname
        }
        self.tcp.sendall(json.dumps(data).encode('utf-8'))
        data = self.tcp.recv(1024).decode('utf-8')
        self.curdir=os.path.basename(data)

    def makedir(self,cmdlist):
        action,dirname=cmdlist
        data = {
            'action': action,
            'dirname':dirname
        }
        self.tcp.sendall(json.dumps(data).encode('utf-8'))
        data = self.tcp.recv(1024).decode('utf-8')
        print(data)


    def response(self):
        data = self.tcp.recv(1024).decode('GBK')
        data = json.loads(data)
        return data


client = ClientHandler()
client.interactive()

 

FTP_server

  1. bean\ ftp_server.py

import  os,sys
PATH= os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from core import  main


if __name__ == '__main__':
    main.ArgvHandler()

 

 

 

     2. conf\ settings.py

IP='localhost'
PORT=8081


import  os

BASE_PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

USERS_PATH=os.path.join(BASE_PATH,'conf','users.txt')

 3. core\ main.py

import optparse
import socketserver

import  os,sys
PATH= os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from conf import  settings
from core.server import  ServerHandler

class ArgvHandler:

    def __init__(self):
        self.op = optparse.OptionParser()
        options, args = self.op.parse_args()
        self.verify_args(options, args)

    def verify_args(self, options, args):
        cmd = args[0]
        if hasattr(self, cmd):
            func = getattr(self, cmd)
            func()

    def start(self):
        tcp = socketserver.ThreadingTCPServer((settings.IP,settings.PORT,), ServerHandler)
        tcp.serve_forever()

 

 

 

 4. core\ server.py

import socketserver
import json, configparser
from conf import settings
import os


class ServerHandler(socketserver.BaseRequestHandler):

    def handle(self):

        while True:
            try:
                data = self.request.recv(1024).strip()
                data = json.loads(data.decode('GBK'))

                if hasattr(self, data.get('action')):
                    func = getattr(self, data.get('action'))
                    func(**data)
                else:
                    print('Invalid ,not found auth method !')

            except Exception as e:
                print(e)
                break

    def auth(self, **data):
        username = data['username']
        password = data['password']

        conf = configparser.ConfigParser()
        conf.read(settings.USERS_PATH)

        if username in conf.sections():
            if conf[username]['Password'] == password:
                self.user=username
                self.mainpath=os.path.join(settings.BASE_PATH,'media',self.user)
                self.send_response(200,'successful')

            else:
                self.send_response(500,'password is error !')

        else:
            self.send_response(500,'username is not exit !')

    def put(self,**data):
        filename=data['filename']
        filesize=data['filesize']
        targetpath=data['targetpath']

        abs_path=os.path.join(self.mainpath,targetpath,filename)

        if os.path.exists(abs_path):
            has_size=os.stat(abs_path).st_size
            if has_size<filesize:
                self.send_response(801,'断点续传!')
                choices=self.request.recv(1024).decode('utf-8')
                if choices =='Y':
                    self.request.sendall(str(has_size).encode('utf-8'))
                    self.write(abs_path,filesize,'ab',has_size=has_size)
                elif choices=='N': # 拒绝断点续传重新上传
                    self.write(abs_path,filesize,'wb')
            else:
                self.send_response(802,'文件已经存在!')
                return
        else:
            self.send_response(800,'文件不存在,可以上传!')
            self.write(abs_path,filesize,'wb')


    def write(self,filename,filesize,mode,has_size=0):
        f=open(filename,mode)
        while has_size<filesize:
            data=self.request.recv(200)
            f.write(data)
            has_size+=len(data)
        f.close()

    def ls(self,**data):
        file_list=os.listdir(self.mainpath)
        file_list='\n'.join(file_list)
        if len(file_list)==0:
            file_list='empty folder!'
        self.request.sendall(file_list.encode('utf-8'))

    def cd(self,**data):
        dirname=data['dirname']
        if dirname =='..':
            self.mainpath=os.path.dirname(self.mainpath)
        else:
            self.mainpath=os.path.join(self.mainpath,dirname)

        self.request.sendall(self.mainpath.encode('utf-8'))

    def makedir(self,**data):
        dirname=data['dirname']
        path=os.path.join(self.mainpath,dirname)
        if not os.path.exists(path):
            if '/' in dirname:
                os.makedirs(path)
            else:
                os.mkdir(path)
            self.request.sendall('文件创建成功'.encode('utf-8'))
        else:
            self.request.sendall('文件已经存在'.encode('utf-8'))

    def send_response(self,state_code,msg):
        response={'state_code':state_code,'msg':msg}
        self.request.sendall(json.dumps(response).encode('GBK'))

 

posted @ 2021-01-06 15:34  leungqingyun  阅读(82)  评论(0编辑  收藏  举报