• Python实现终端FTP文件传输


    实现终端FTP文件传输

    代码结构:

    .
    ├── client.py
    ├── readme.txt
    └── server.py

    运行截图:

    readme.txt

    tftp文件服务器
    
    项目功能:
        * 客户端有简单的页面命令提示
        * 功能包含:
            1、查看服务器文件库中的文件列表(普通文件) -> os.listdir
            2、可以下载其中的某个文件到本地
            3、可以上传客户端文件到服务器文件库
        * 服务器需求:
            1、允许多个客户端同时操作
            2、每个客户端可能会连续发送命令
    
    技术分析:
        1、TCP套接字更适合文件传输
        2、并发方案 -> fork多进程并发
        3、对文件的读写操作
        4、获取文件列表 -> os.listdir() 或 tree
        5、粘包的处理
    
    整体结构设计:
        1、服务器功能封装在类中(上传,下载,查看列表)
        2、创建套接字,流程函数调用main()
        3、客户端负责发起请求,接收回复,展示
        4、服务端负责接受请求,逻辑处理
    
    编程实现:
        1、搭建整体结构,创建网络连接
        2、创建多进程和类的结构
        3、每个功能模块的实现
    
    模块方法:
        os.listdir(path)
        os.path.isfile()
        os.path.isdir()
        

    server.py

    # server.py
    
    import struct
    from socket import *
    import os
    import signal
    import sys
    import time
    
    # 文件库
    FILE_PATH = '/home/noon/Python/Example/'
    
    # 实现功能模块
    class TftpServer(object):
        def __init__(self, sockfd, addr):
            super().__init__()
            self.sockfd = sockfd
            self.addr = addr
            self.opt = ''
    
        def display(self):
            re = ''
            for i in os.listdir(FILE_PATH):
                re += i + '
    '
            self.sockfd.send(re.encode())
    
        def download(self):
            '下载模块功能实现'
            # 尝试打开文件
            filename = FILE_PATH + self.opt.split(' ')[1]
            print(filename)
            try:
                fp = open(filename, 'rb')
            except:
                self.sockfd.send(b'Failed to open file')
            else:
                self.sockfd.send(b'Ready to transfer')
                # 循环发送数据
                while True:        
                    data = fp.read(1024)        
                    if not data:
                        # 如果传输完毕,data为空,传输0,跳出循环
                        res = struct.pack('i', 0)
                        self.sockfd.send(res)
                        break
                    res = struct.pack('i', len(data))
                    self.sockfd.send(res)
                    self.sockfd.send(data)
                print('Done!')
    
        def upload(self):
            filename = FILE_PATH + self.opt.split(' ')[1]
            try:
                fp = open(filename, 'wb')
            except:
                self.sockfd.send('Unable to open file'.encode())
            else:
                self.sockfd.send(b'Ready to upload')
                while True:
                    res = self.sockfd.recv(4)
                    length = struct.unpack('i', res)[0]
                    if length == 0:
                        break
                    data = self.sockfd.recv(length)
                    fp.write(data)
                fp.close()
                print('Done!')
    
    
        def quit(self):
            print(self.addr, '断开连接')
            self.sockfd.close()
            sys.exit()
    
    # 主流程
    def main():
        HOST = '0.0.0.0'
        PORT = 5555
        ADDR = (HOST, PORT)
    
        sockfd = socket()
        sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        sockfd.bind(ADDR)
        sockfd.listen(5)
    
        # 通知内核对子进程的结束不关心,由内核回收。
        signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    
        while True:
            try:
                connfd, addr = sockfd.accept()
            except KeyboardInterrupt:
                sockfd.close()
                sys.exit('服务器退出')
            except Exception as e:
                print(e)
                continue
    
            print('连接成功:', addr)
    
            # 创建子进程
            pid = os.fork()
    
            if pid == 0:
                sockfd.close()
                tftp = TftpServer(connfd, addr)
                while True:
                    tftp.opt = connfd.recv(1024).decode()
                    if tftp.opt == 'display':
                        tftp.display()
                    elif tftp.opt.startswith('download'):
                        tftp.download()
                    elif tftp.opt.startswith('upload'):
                        tftp.upload()
                    elif tftp.opt == 'quit':
                        tftp.quit()
            else:
                connfd.close()
                continue
    
    
    if __name__ == '__main__':
        main()

    client.py

    # client.py
    
    from socket import *
    import sys
    import time
    import struct
    
    # 实现各种功能请求
    class TftpClient(object):
        def __init__(self, sockfd):
            super().__init__()
            self.sockfd = sockfd
            self.opt = ''
    
        def panel(self):
            print('+', '*'*30, '+', sep='')
            print('+', 'display'.center(30), '+', sep='')
            print('+', 'download'.center(30), '+', sep='')
            print('+', 'upload'.center(30), '+', sep='')
            print('+', 'quit'.center(30), '+', sep='')
            print('+', '*'*30, '+', sep='')
    
        def display(self):
            self.sockfd.send(b'display')
            print(self.sockfd.recv(1024).decode())
    
        def download(self):
            '客户端下载请求'
            # 先使用display命令向服务器请求文件列表,验证用户想要下载的文件是否存在
            filename = input('filename>> ')
            if not filename:
                return
            self.sockfd.send(b'display')
            files = self.sockfd.recv(1024).decode().split('
    ')
            if not filename in files:
                print('Cannot locate', filename)
                return
            # 文件存在,发送下载请求到服务端,并接收返回结果
            data = 'download ' + filename
            self.sockfd.send(data.encode())
            data = self.sockfd.recv(1024).decode()
            # 如果服务端无法打开文件
            if data == 'Failed to open file':
                print('Failed to open file')
            # 可以执行下载操作
            else:
                # 调用写方法
                print(data)
                self.write(filename)
                print('Done!')
    
        def write(self, filename):
            '从服务器下载文件'
            # 考虑到粘包问题,导入struct模块,接收服务端要发送的数据的大小,再按照这个大小接收数据,循环执行
            fp = open(filename, 'wb')
            while True:
                # 接收数据大小,调用struct.unpack方法获得数据大小
                res = self.sockfd.recv(4)
                length = struct.unpack('i', res)[0]
                # 如果数据大小为0,说明传输结束,退出循环
                if length == 0:
                    break
                # 按照数据的大小接收数据
                data = self.sockfd.recv(length)
                fp.write(data)
            fp.close()
    
        def upload(self):
            # 文件路径
            filepath = input('filepath>> ')
            try:
                fp = open(filepath, 'rb')
            except:
                print('Unable to open', filepath)
                return
            else:
                # 文件上传要保存为什么名字
                # 先使用display命令向服务器请求文件列表,验证用户想要上传的文件名是否存在
                filename = input('filename>> ')
                if not filename:
                    return
                self.sockfd.send(b'display')
                files = self.sockfd.recv(1024).decode().split('
    ')
                if filename in files:
                    print('File already exists!')
                    return
                # 可以上传
                data = 'upload ' + filename
                self.sockfd.send(data.encode())
                data = self.sockfd.recv(1024).decode()
                if data == 'Unable to open file':
                    print('服务器打开文件出错')
                    return 
                else:
                    self.read(fp)
    
        def read(self, fp):
            '读取文件上传服务器'
            while True:
                data = fp.read(1024)
                if not data:
                    res = struct.pack('i', 0)
                    self.sockfd.send(res)
                    break
                res = struct.pack('i', len(data))
                self.sockfd.send(res)
                self.sockfd.send(data)
            print('Done!')
    
        def quit(self):
            self.sockfd.send(b'quit')
            self.sockfd.close()
            sys.exit('客户端关闭')
    
    # 创建套接字,建立连接
    def main():
        argc = len(sys.argv)
        if argc != 3:
            sys.exit('Usage: python client.py host port')
        else:
            HOST = sys.argv[1]
            PORT = int(sys.argv[2])
            ADDR = HOST, PORT
    
            sockfd = socket()
            try:
                sockfd.connect(ADDR)
            except ConnectionRefusedError:
                sys.exit('无法连接到服务端')
    
            tftp = TftpClient(sockfd)
    
            tftp.panel()
            while True:
                try:
                    tftp.opt = input('>> ').lower()
                except KeyboardInterrupt:
                    tftp.quit()
                if tftp.opt == 'display':
                    tftp.display()
                elif tftp.opt == 'download':
                    tftp.download()
                elif tftp.opt == 'upload':
                    tftp.upload()
                elif tftp.opt == 'quit':
                    tftp.quit()
                else:
                    continue
    
    
    if __name__ == '__main__':
        main()
    Resistance is Futile!
  • 相关阅读:
    找到IOS中的闪退日志
    day10-单元测试用例
    1、MySQL索引优化分析
    大话处理器-第2章-初识处理器
    sunset: dusk
    CK: 00
    My File Server: 1
    [luogu 5049] 旅行(数据加强版)
    [luogu p1081] 开车旅行
    [luogu p1613] 跑路
  • 原文地址:https://www.cnblogs.com/noonjuan/p/11314736.html
Copyright © 2020-2023  润新知