项目整理——利用socket网络编程实现FTP(核心代码)

难点:1.客户端发送的请求,利用管道符进行拼接,方便服务端进行切割
2.根据发送的大小,以及传送的数据量来计算百分比,实现进度显示
3.利用反射,来将字符串变为函数命令,执行程序
4.利用ab和wb来实现覆盖和续传

客户端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import os
import re
import json
from config import settings
from lib import commons


def login(conn):
    while True:
        username = input('请输入用户名(q退出):')
        pwd = input('请输入密码:')
        login_info = {'username': username, 'pwd': pwd}
        conn.sendall(bytes(json.dumps(login_info), 'utf-8'))
        if str(conn.recv(1024), encoding='utf-8') == "4002":
            print('授权成功...')
            break
        else:
            print('用户名或密码错误...')


def cmd(conn, inp):
    conn.sendall(bytes(inp, 'utf-8'))
    basic_info_bytes = conn.recv(1024)
    basic_info_str = str(basic_info_bytes, 'utf-8')
    if basic_info_str == '4001':
        login(conn)
    else:
        conn.sendall(bytes("ack", 'utf-8'))
        print(str(basic_info_bytes, 'utf-8'))
        result_length = int(str(basic_info_bytes, 'utf-8').split('|')[1])
        has_received = 0
        content_bytes = bytes()
        while has_received < result_length:
            fetch_bytes = conn.recv(1024)
            has_received += len(fetch_bytes)
            content_bytes += fetch_bytes
        cmd_result = str(content_bytes, 'utf-8')
        print(cmd_result)


def post(conn, inp):
    # inp  ===> post|D:/1.txt     2.txt
    method, file_paths = inp.split("|", 1)
    local_path, target_path = re.split('\s*', file_paths, 1)
    # 获取本地文件大小
    file_byte_size = os.stat(local_path).st_size
    # 获取本地文件名
    file_name = os.path.basename(local_path)
    # 获取本地文件md5值
    file_md5 = commons.fetch_file_md5(local_path)

    post_info = "post|%s|%s|%s|%s" % (file_byte_size, file_name, file_md5, target_path)

    conn.sendall(bytes(post_info, 'utf-8'))

    result_exist = str(conn.recv(1024), 'utf-8')

    if result_exist == '4001':
        login(conn)
        return

    has_sent = 0
    if result_exist == "2003":
        inp_continue = input('文件已经存在,是否续传?Y/N')
        if inp_continue.upper() == "Y":
            conn.sendall(bytes('2004', 'utf-8'))
            result_continue_pos = str(conn.recv(1024), 'utf-8')
            has_sent = int(result_continue_pos)
        else:
            conn.sendall(bytes('2005', 'utf-8'))

    file_obj = open(local_path, 'rb')
    file_obj.seek(has_sent)
    while file_byte_size > has_sent:
        data = file_obj.read(1024)
        conn.sendall(data)
        has_sent += len(data)
        commons.bar(has_sent, file_byte_size)
    file_obj.close()
    print('上传成功')


def get(conn, inp):
    pass


def help_info():
    print("""
            cmd|命令
            post|文件路径
            get|下载文件路径
            exit|退出
        """)


def execute(conn):
    choice_dict = {
        'cmd': cmd,
        'get': get,
        'post': post,
    }
    help_info()
    while True:
        # cmd|ls
        # post|本地路径 服务器上路径
        # get|服务器路径 本地路径
        inp = input("please input:")
        if inp == 'help':
            help_info()
            continue
        choice = inp.split('|')[0]
        if choice == 'exit':
            return
        if choice in choice_dict:
            func = choice_dict[choice]
            func(conn, inp)


def main():
    ip_port = (settings.server, settings.port)
    conn = socket.socket()
    conn.connect(ip_port)
    welcome_bytes = conn.recv(1024)
    print(str(welcome_bytes, encoding='utf-8'))

    execute(conn)

    conn.close()




服务端

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import subprocess
import re
import os
import json
import socketserver

from config import settings

ACTION_CODE = {
    '1000': 'cmd',
    '2000': 'post',
    '3000': 'get',
}

REQUEST_CODE = {
    '1001': 'cmd info',
    '1002': 'cmd ack',
    '2001': 'post info',
    '2002': 'ACK(可以开始上传)',
    '2003': '文件已经存在',
    '2004': '续传',
    '2005': '不续传',
    '3001': 'get info',
    '3002': 'get ack',
    '4001': "未授权",
    '4002': "授权成功",
    '4003': "授权失败"
}


class Server(object):
    request_queue_size = 5

    def __init__(self):
        self.socket = socket.socket()
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        try:
            self.server_bind(settings.BIND_HOST, settings.BIND_PORT)

            self.server_activate()
        except Exception as e:
            print(e)
            self.server_close()

    def server_bind(self, ip, port):
        self.socket.bind((ip, port,))

    def server_activate(self):
        self.socket.listen(self.request_queue_size)
        self.run()

    def server_close(self):
        self.socket.close()

    def run(self):
        # get request
        while True:
            conn, address = self.socket.accept()
            conn.sendall(bytes("欢迎登陆", 'utf-8'))
            obj = Action(conn)
            while True:
                client_bytes = conn.recv(1024)
                if not client_bytes:
                    break

                client_str = str(client_bytes, encoding='utf-8')
                if obj.has_login:

                    o = client_str.split('|', 1)
                    if len(o) > 0:
                        func = getattr(obj, o[0])
                        func(client_str)
                    else:
                        conn.sendall(bytes('输入格式错误', 'utf-8'))
                else:
                    obj.login(client_str)

            conn.close()


class MultiServerHandler(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        conn.sendall(bytes("欢迎登陆", 'utf-8'))
        obj = Action(conn)
        #  self.conn = conn
        # self.has_login = False
        # self.username = None
        # self.home = None
        # self.current_dir = None
        while True:
            client_bytes = conn.recv(1024)
            if not client_bytes:
                break
            # cmd|ipconfig
            client_str = str(client_bytes, encoding='utf-8')
            if obj.has_login:
                o = client_str.split('|', 1)
                # o[0]: cmd
                if len(o) > 0:
                    func = getattr(obj, o[0])
                    func(client_str) # cmd|ipconfig
                else:
                    conn.sendall(bytes('输入格式错误', 'utf-8'))
            else:
                obj.login(client_str)

        conn.close()


class MultiServer(object):
    def __init__(self):
        server = socketserver.ThreadingTCPServer((settings.BIND_HOST, settings.BIND_PORT), MultiServerHandler)
        server.serve_forever()


class Action(object):
    def __init__(self, conn):
        self.conn = conn
        self.has_login = False
        self.username = None
        self.home = None
        self.current_dir = None

    def login(self, origin):
        self.conn.sendall(bytes("4001", 'utf-8'))
        while True:
            login_str = str(self.conn.recv(1024), encoding='utf-8')
            login_dict = json.loads(login_str)
            if login_dict['username'] == 'wupeiqi' and login_dict['pwd'] == '123':
                self.conn.sendall(bytes("4002", 'utf-8'))
                self.has_login = True
                self.username = 'wupeiqi'
                self.initialize()
                break
            else:
                self.conn.sendall(bytes("4003", 'utf-8'))

    def initialize(self):
        self.home = os.path.join(settings.USER_HOME, self.username)
        self.current_dir = os.path.join(settings.USER_HOME, self.username)

    def cmd(self, origin):

        func, command = origin.split('|', 1)
        command_list = re.split('\s*', command, 1)

        if command_list[0] == 'ls':
            if len(command_list) == 1:
                if self.current_dir:
                    command_list.append(self.current_dir)
                else:
                    command_list.append(self.home)
            else:
                if self.current_dir:
                    p = os.path.join(self.current_dir, command_list[1])
                else:
                    p = os.path.join(self.home, command_list[1])
                command_list[1] = p

        if command_list[0] == 'cd':
            if len(command_list) == 1:
                command_list.append(self.home)

            else:
                if self.current_dir:
                    p = os.path.join(self.current_dir, command_list[1])
                else:
                    p = os.path.join(self.home, command_list[1])
                self.current_dir = p
                command_list[1] = p
        command = ' '.join(command_list)
        try:
            result_bytes = subprocess.check_output(command, shell=True)
            # result_bytes # gbk字节
            result_bytes = bytes(str(result_bytes, encoding='gbk'), encoding='utf-8')
        except Exception as e:
            result_bytes = bytes('error cmd', encoding='utf-8')



        info_str = "info|%d" % len(result_bytes)
        self.conn.sendall(bytes(info_str, 'utf-8'))
        ack = self.conn.recv(1024)
        self.conn.sendall(result_bytes)

    def post(self, origin):
        func, file_byte_size, file_name, file_md5, target_path = origin.split('|', 4)
        target_abs_md5_path = os.path.join(self.home, target_path)
        has_received = 0
        file_byte_size = int(file_byte_size)

        if os.path.exists(target_abs_md5_path):
            self.conn.sendall(bytes('2003', 'utf-8'))
            is_continue = str(self.conn.recv(1024), 'utf-8')
            if is_continue == "2004":
                has_file_size = os.stat(target_abs_md5_path).st_size
                self.conn.sendall(bytes(str(has_file_size), 'utf-8'))
                has_received += has_file_size
                f = open(target_abs_md5_path, 'ab')

            else:
                f = open(target_abs_md5_path, 'wb')
        else:
            self.conn.sendall(bytes('2002', 'utf-8'))
            f = open(target_abs_md5_path, 'wb')

        while file_byte_size > has_received:
            data = self.conn.recv(1024)
            f.write(data)
            has_received += len(data)
        f.close()

    def get(self, origin):
        pass

    def exit(self, origin):
        pass




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值