python 之selectors 实现文件上传下载

本文介绍了一个基于Python的简易FTP服务端与客户端实现方案,支持文件上传和下载功能,利用selector机制实现多客户端并发处理。注意该版本不支持断点续传等功能。

1.文件目录

selector_FTP ——————

       clientfile ——————  存放用户文件路径

       selector_client.py——

selector_server.py——

       serverfile —————— 存放服务器文件路径

 

注:实现的简约版的FTP,不支持断点续传、断点下载等操作

且只支持普通上传下载 用selector实现,支持多条客户端链接同时下载、上传 ,切勿多个客户端同时上传、下载同一个文件。

 

服务端

selector_server.py

def get(conn,cmd): 下载文件

def put(conn,cmd): 上传文件

def accept(sock, mask): selector 注册方法

def read(conn, mask): selector 注册方法

selector_server.py

def get(conn,cmd): 下载文件

def put(conn,cmd): 上传文件

def progress(recive_size,file_size,tips) 显示下载进度方法

 

例子:

clientfile 里面有一个3D.pdf  在客户端输入命令 put 3D.pdf  后 serverfile 文件夹下会有3D.pdf 

serverfile 里面有一个test.zip  在客户端输入命令 get test.zip  后 clientfile  文件夹下会有test.zip

服务器端:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wenjinLi
import selectors
import socket
import os,sys
import time
import errno
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sel = selectors.DefaultSelector()

def get(conn,cmd):
    if len(cmd.split()) > 1:
        filename = cmd.split()[1]
        file_path = '%s/serverfile/%s' % (BASE_DIR, filename) #文件路径
        print(file_path)
        if os.path.isfile(file_path): #判断文件是否存在
            client_file_size = 0
            file_size = os.stat(file_path).st_size #获得文件大小
            conn.send(str(file_size).encode()) #将文件大小传给客户端
            time.sleep(0.3)
            while True:
                with open(file_path, 'rb') as f:
                    try:
                        for line in f:
                            client_file_size += len(line) #记录已经传送的文件大小
                            conn.sendall(line)
                            print(client_file_size, file_size)
                    except IOError as e:
                        if e.errno != errno.EAGAIN: # errno.EAGAIN 缓冲区满 等待下
                            raise
                        else:
                            time.sleep(0.1) #等待0.1s进行下一次读取
                if client_file_size >= int(file_size): #文件传送完毕
                    print('break')
                    break
        else:
            conn.sendall('405'.encode())  # 文件不存在

    else:
        conn.sendall('401'.encode())  # 命令不存在,无法操作
def put(conn,cmd):
    filename = cmd.split()[1]
    filename = "%s/serverfile/%s" % (BASE_DIR, filename) #文件路径
    with open(filename, 'ab') as f:
        data = conn.recv(1024).decode()
        total_size = int(data)
        if total_size == '405' or total_size == '401': #文件不存在 或命令有误
            return None
        current_size = 0
        while current_size <= total_size:
            try:
                recivesize = total_size - current_size
                if recivesize > 1024:
                    recivesize = 1024
                data = conn.recv(recivesize)
                if not data: #客户端断开
                    print('closing', conn)
                    sel.unregister(conn)
                    conn.close()
                    break
                current_size += len(data)
                f.write(data)
            except IOError as e:
                if e.errno != errno.EAGAIN:
                    raise
                else:
                    time.sleep(0.1)
        print('接收完毕')

def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    conn.setblocking(False)
    sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print(data)
        command_str = data.decode()
        print(command_str)
        if command_str.split()[0] == 'put':
            put(conn, command_str)
        elif command_str.split()[0] == 'get':
            get(conn,command_str)
        else:
            conn.send(b'404') # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()

sock = socket.socket()
sock.bind(('localhost', 1112))
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)

 用户端:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: wenjinLi
import socket
import time
import os,sys
import errno
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
def get(s,command):
    '''下载'''
    if len(command.split()) == 2:
        s.send(command.encode()) #通知服务器 要上传文件了
    else:
        print('下载命令有误')
    filename = command.split()[1]
    filename = "%s/clientfile/%s" % (BASE_DIR, filename) #本地地址
    with open(filename, 'ab') as f:
        data = s.recv(1024)
        print(data)
        data = data.decode()
        total_size = int(data)
        if total_size == '405':
            print('文件不存在')
            return None
        current_size = 0
        while current_size <= total_size:
            try:
                recivesize = total_size - current_size
                if recivesize > 1024:
                    recivesize = 1024
                data = s.recv(recivesize)
                if not data:
                    break
                current_size += len(data)
                progress(current_size, total_size, '下载中...') #打印进度
                print(current_size, total_size)
                f.write(data)
            except IOError as e:
                if e.errno != errno.EAGAIN:
                    raise
                else:
                    time.sleep(0.1) #若 系统缓存已满则等带0.1秒后开始
        print('接收完毕')
def put(s,command):
    '''上传'''
    if len(command.split()) == 2:
        s.send(command.encode()) #告诉服务器要上传文件
    else:
        print('上传命令有误')
    if len(command.split()) > 1:
        filename = command.split()[1]
        file_path = '%s/clientfile/%s' % (BASE_DIR, filename)
        print(file_path)
        if os.path.isfile(file_path):
            current_file_size = 0
            file_size = os.stat(file_path).st_size

            s.send(str(file_size).encode())
            time.sleep(0.3)
            with open(file_path, 'rb') as f:
                try:
                    for line in f:
                        s.sendall(line)
                        current_file_size += len(line)
                        progress(current_file_size, file_size, '上传中...')
                        print(current_file_size,file_size)
                except IOError as e:
                    if e.errno != errno.EAGAIN:  # errno.EAGAIN 缓冲区满 等待下
                        raise
                    else:
                        time.sleep(0.1)  # 等待0.1s进行下一次读取
        else:
            s.send('405'.encode())
            print('文件不存在')

    else:
        s.send('401'.encode())
        print('命令不存在')

def progress(recive_size,file_size,tips):
    '''***代表加载 _____表示还未加载部分'''
    lensize = 100
    percent = float(recive_size) / float(file_size)
    recivenumber = '*' * int(percent * lensize)  # 已经加载的所占格数
    leftnumber = '_' * (lensize - int(percent * lensize))  # 未加载的所占格数
    sys.stdout.write('%s:%d%% [%s]\n' % (tips, percent * lensize, recivenumber + leftnumber))
    sys.stdout.flush()

if __name__ == '__main__':

    HOST = 'localhost'
    PORT = 1112
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((HOST,PORT))
    while True:
        msg = input('>>>请输入操作命令:').strip()
        if len(msg) == 0:
            continue
        command = msg.split()[0]
        if command == 'get':
            '''下载'''
            get(s,msg)
        elif command == 'put':
            '''上传'''
            put(s, msg)
        elif command == 'exit':
            print('成功退出')
            break
        else:
            continue

    s.close()

 

转载于:https://www.cnblogs.com/nanfengxiaojin/p/6942243.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值