Python3代码-selctors实现文件上传与下载

server.py

import selectors
import socket
import os
import time


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

class selectFtpserver:
    def __init__(self):
        self.dic = {}  # 创建空字典
        self.hasReceived = 0
        self.hasSend=0
        self.sel = selectors.DefaultSelector()  # 生成一个select对象
        self.create_socket() #create_socket()是创建socket对象函数完成绑定功能
        self.hanle()  #handle()函数完成循环监听

    def create_socket(self):
        sock = socket.socket()
        sock.bind(('127.0.0.1', 8899))
        sock.listen()
        sock.setblocking(False)
        self.sel.register(sock, selectors.EVENT_READ, self.accept)  # 把刚生成的sock连接对象注册到select连接列表中,并交给accept函数处理
        print("服务端已打开,请连接客户端")

    def hanle(self):
        while True:
            events = self.sel.select()  # 默认是阻塞,有活动连接就返回活动的连接列表
            # 这里看起来是select,其实有可能会使用epoll,如果你的系统支持epoll,那么默认就是epoll
            # print("event==",events)
            for key, mask in events:
                callback = key.data  # 去调accept函数
                callback(key.fileobj, mask)  # key.fileobj就是readable中的一个socket连接对象

    def accept(self,sock, mask):
        conn, addr = sock.accept()  # Should be ready
        print('accepted', conn, 'from', addr)
        conn.setblocking(False)  # 设定非阻塞
        self.sel.register(conn, selectors.EVENT_READ, self.read)  # 新连接注册read回调函数
        self.dic[conn] = {}  # 在空字典里进行了conn赋值,self.dic={conn:{},}

    def read(self, conn, mask):  # 接收了conn和mask
        try:  # 加异常防止客户端突然断开
            if not self.dic[conn]:  # 判断self.dic[conn]里面是否是空字典,如果是空字典,代表第一次进来
                print('====第一次进来')
                data = conn.recv(1024)  # conn接收了客户端发来的数据
                print("data==",str(data, encoding='utf-8'))
                cmd, filename,filesize = str(data, encoding='utf-8').split('|')  # 把接收到客户端发来的包解开拿到cmd,filename,filesize个信息
                self.dic = {conn: {"cmd": cmd, "filename": filename, "filesize": int(filesize)}}  # 把拿到的cmd,filename,filesize信息放到self.dic字典里去后程序返回到handle()函数里的events继续监听
                print(self.dic)
                if cmd == 'put':  # 如果接收的信息是put
                    conn.send(bytes("OK", encoding='utf8'))  # 给客户端返回一条数据
                if self.dic[conn]['cmd'] == 'get':
                    file = os.path.join(BASE_DIR, "upload", filename)

                    if os.path.exists(file):
                        print("文件存在的情况,返回YES给客户端")
                        filesize = os.path.getsize(file)
                        self.dic[conn]['filesize'] = filesize
                        print("self.dic",self.dic)
                        send_info = '%s|%s' % ('YES', filesize)
                        conn.send(bytes(send_info, encoding='utf8'))
                    else:
                        print("文件不存在情况下")
                        send_info = '%s|%s' % ('NO', 0)
                        conn.send(bytes(send_info, encoding='utf8'))
                        self.dic[conn] = {} #文件不存在的情况下,要将清空字典
            else:  # 如果不是空字典代表不是第一次进来
                print('不是第一次来的')
                print(self.dic)
                if self.dic[conn].get('cmd', None):  # 对接收的命令进行分发判断是put还是get
                    cmd = self.dic[conn].get('cmd')
                    if hasattr(self, cmd):  # 如果cmd=put调用put函数,如果是cmd=get函数调用get函数
                        func = getattr(self, cmd)
                        func(conn)
                    else:
                        print("error cmd!")
                        conn.close()
                else:
                    print("error cmd!")
                    conn.close()
        except Exception as e:
            print('断开的客户端信息是:', conn)
            self.sel.unregister(conn)  # 如果没有接收到数据做一个关闭解除
            conn.close()

        # put上传函数
    def put(self, conn):
        fileName = self.dic[conn]['filename']
        fileSize = self.dic[conn]['filesize']
        # print("BASE_DIR",BASE_DIR)
        path = os.path.join(BASE_DIR, "upload", fileName)  # 拿到要接收的信息
        # print(fileName,fileSize,path)

        recv_data = conn.recv(1024)  # 接收客户端上传的数据1024字节
        self.hasReceived += len(recv_data)  # 把接收的数据累加到变量self.hasReceived

        with open(path, 'ab') as f:  # 打开文件
            f.write(recv_data)  # 把接收的数据写到文件里去

        if fileSize == self.hasReceived:  # 判断文件大小跟接收大小是否一样
            if conn in self.dic.keys():  # 如果文件大小跟接收大小一样清空字典
                self.dic[conn] = {}
            self.hasReceived = 0  #S上传结束之后,需要将self.hasReceived 重置成功
            print("%s 上传完毕!" % fileName)

    def get(self,conn):
        fileName = self.dic[conn]['filename']
        file = os.path.join(BASE_DIR, "upload", fileName)
        # fileSize = os.path.getsize(file)
        fileSize=self.dic[conn]['filesize']

        data = conn.recv(1024)  # conn接收了客户端发来的数据
        dataOK = str(data, encoding='utf-8')

        if dataOK == 'OK':
            with open(file, 'rb') as f:  # 打开文件
                while fileSize > self.hasSend:  # 循环的发送文件给客户端
                    contant = f.read(1024)
                    recv_size = len(contant)
                    conn.send(contant)
                    self.hasSend += recv_size
                    s = str(int(self.hasSend / fileSize * 100)) + "%"
                    print("正在下载文件: " + fileName + " 已经下载:" + s)

            if fileSize == self.hasSend:  # 判断文件大小跟接收大小是否一样
                if conn in self.dic.keys():  # 如果文件大小跟接收大小一样清空字典
                    self.dic[conn] = {}
                print("%s 下载完毕!" % fileName)
                self.hasSend = 0

if __name__ == '__main__':
    selectFtpserver()

client.py

import socket
import os,sys
BASE_DIR=os.path.dirname(os.path.abspath(__file__))

class selectFtpClient:
    def __init__(self):
        self.args=sys.argv                              #sys.argv在命令行输入的参数,第一个参数默认文件名,第二个参数跟IP地址和端口
        if len(self.args)>1:                            #如果大于1把第二个参数俩个值赋值给port
            self.port=(self.args[1],int(self.args[2]))
        else:
            self.port=("127.0.0.1",8899)               #如果没有第二个参数默认取这个
        self.create_socket()                             #
        self.command_fanout()                            #进行命令分发
        self.mainPath = os.path.join(BASE_DIR, 'filename')  # 获取该客户端下的filename路径

    #create_socket函数创建socket对象连接服务端
    def create_socket(self):
        try:
            self.sk = socket.socket()
            self.sk.connect(self.port)
            print('连接FTP服务器成功!')
        except Exception as e:
            print("eroor:",e)

    #command_fanout()函数进行命令分发
    def command_fanout(self):
        while True:
            try:
                print("----------------welcome to ftp client-------------------")
                self.help_info()
                cmd_info = input('>>>请输入操作命令:').strip()  # put 12.png images
                if not cmd_info:
                    continue
                cmd,file = cmd_info.split()  ##按照空格分隔
                # print("命令是什么", cmds)
                if cmd == "quit":
                    break
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func(cmd,file)
                    Tag = input("是否继续进入ftp clinet,请选择Y/N:").strip()
                    if Tag.upper() == 'Y':
                        continue
                    else:
                        break
                else:
                    print('No such command ,please try again')
            except Exception as e:  # server关闭了
                print('%s' % e)
                break

    def help_info(self):
        print ('''
              get + (文件名)    表示下载文件
              put + (文件名)    表示上传文件
              quit              表示退出登录
        ''')

    #put()上传函数
    def put(self,cmd,file):
        if os.path.isfile(file):                            #判断本地文件是否存在
            fileName = os.path.basename(file)                #取出文件的名字
            fileSize = os.path.getsize(file)                 #取出文件的大小
            fileInfo = '%s|%s|%s'%(cmd,fileName,fileSize)  #给文件名字大小打包成fileInf
            self.sk.send(bytes(fileInfo, encoding='utf8'))  #调用send方法把fileInf发给服务端
            recvStatus = self.sk.recv(1024)                  #接收服务端返回的OK内容
            print('recvStatus' , recvStatus)
            hasSend = 0
            if str(recvStatus, encoding='utf8') == "OK":   #如果接收到服务端返回的OK
                with open(file, 'rb') as f:                #打开文件
                    while fileSize > hasSend :              #循环的去上传文件
                        contant = f.read(1024)
                        recv_size = len(contant)
                        self.sk.send(contant)
                        hasSend += recv_size
                        s=str(int(hasSend/fileSize*100))+"%"
                        print("正在上传文件: "+fileName+" 已经上传:" +s)
                print('%s文件上传完毕' % (fileName,))
        else:
            print('要上传的文件不存在')

    #get()下载函数
    def get(self,cmd,fileName):
        path = os.path.join(BASE_DIR, "download", fileName)  # 拿到要接收的信息
        fileSize=0
        fileInfo = '%s|%s|%s' % (cmd, fileName, fileSize)  # 给文件名字大小打包成fileInf
        print(fileInfo)
        self.sk.send(bytes(fileInfo, encoding='utf8'))  # 调用send方法把fileInfo发给服务端

        recvdata = self.sk.recv(1024)  # 接收服务端返回的是否存在文件内容
        recvStatus, fileSize = str(recvdata, encoding='utf-8').split('|')
        print("recvStatus==",recvStatus,fileSize)
        fileSize = int(fileSize)

        hasReceived = 0
        if recvStatus == "YES":  # 如果接收到服务端返回的YES
            self.sk.send(bytes('OK', encoding='utf8'))  # 通知服务端可以正常下载了

            while fileSize > hasReceived:  # 循环的发送文件给客户端
                recv_data = self.sk.recv(1024)  # 接收客户端上传的数据1024字节
                hasReceived += len(recv_data)  # 把接收的数据累加到变量self.hasReceived
                print("hasReceived",hasReceived)

                with open(path, 'ab') as f:  # 打开文件
                    f.write(recv_data)  # 把接收的数据写到文件里去

                if fileSize == hasReceived:  # 判断文件大小跟接收大小是否一样
                    print("%s 下载完毕!" % fileName)
                    recvStatus = 'YESS'
        else:
             print('要下载的文件不存在')


if __name__=='__main__':
    selectFtpClient()
原文地址:https://www.cnblogs.com/sugh/p/13930873.html