python-selctors实现文件上传

服务端代码:程序目录server/server.py   上传文件目录:server/upload

import os
import time
import socket
import selectors                     #封装了一些相应的操作

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

#第二步:
class selectFtpServer:
    def __init__(self):
        self.dic = {}                            #创建空字典
        self.hasReceived=0
        self.sel = selectors.DefaultSelector()   #通过selectors模块下的DefaultSelector这个类拿I/O多路方法拿到实例对象变成实例变量self.sel
        self.create_socket()                     #create_socket()是创建socket对象函数完成绑定功能
        self.handle()                            #handle()函数完成循环监听

    #create_socket函数是用来创建socket对象完成绑定功能
    def create_socket(self):
        server = socket.socket()                                      #创建socket对象赋值server
        server.bind(('192.168.1.160', 8080))                        #绑定
        server.listen(5)                                              #监听
        server.setblocking(False)                                    #设置非阻塞
        self.sel.register(server, selectors.EVENT_READ,self.accept)   #有了server对象通过self.sel实例变量register注册完成绑定功能(server跟self.accept做绑定)
        print("服务端已启动,等待客户端连接.....")

    #handle()函数完成循环监听
    def handle(self):
        while True:                        #第一步:程序启动后走while循环                                       #第七步:运行完accept函数后到这里
            #所有操作围绕一个对象self.sel实例变量核心对象展开的
            events = self.sel.select()       #第二步:调用self.sel实例变量,监听的内容server封装到events对象里    #第八步:如果客户端发过来此刻监听的内容就有变化有俩个对象server和conn封装到events对象里
            for key, mask in events:        #第三步:for循环events(可迭代对象)拿到key和mask                      #第九步:for循环events(可迭代对象)拿到key和mask
                callback = key.data          #第四步:当前key.data是self.accept函数赋值给callback                 #第十步:当前key.data是read函数赋值给callback
                #key.fileobj是拿到的监听的对象。
                callback(key.fileobj, mask)  #第五步:运行callback执行accpt()函数                                 #第十一步:运行callback执行read()函数里面放到的之前链接相应的文件描述符conn
                #print('当前监听的对象:', key.fileobj)

    #第六步:accept函数接收连接服务端的客户端信息
    def accept(self,sock,mask):                                   #接收sock和mask
        conn, addr = sock.accept()                                 #sock.accept接收此时链接服务端的socket对象拿到conn和addr
        #print('accepted', conn, 'from', addr)
        print("from %s %s connected" %addr)
        conn.setblocking(False)                                   #设置成非阻塞
        self.sel.register(conn, selectors.EVENT_READ, self.read)   #把conn跟read做绑定后程序返回到handle()函数里的events继续监听
        #print(conn)

        self.dic[conn] = {}                                        #在空字典里进行了conn赋值,self.dic={conn:{},}

    #第十二步:read函数返回给客户端ok
    def read(self,conn, mask):                                                  #接收了conn和mask
        try:                                                                    #加异常防止客户端突然断开
            if not self.dic[conn]:                                              #判断self.dic[conn]里面是否是空字典,如果是空字典,代表第一次进来
                data = conn.recv(1024)                                           #conn接收了客户端发来的数据
                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继续监听

                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):
                        fileSize = os.path.getsize(file)
                        send_info = '%s|%s' %('YES',fileSize)
                        conn.send(bytes(send_info, encoding='utf8'))
                    else:
                        send_info = '%s|%s' %('NO',0)
                        conn.send(bytes(send_info, encoding='utf8'))

            else:                                                #如果不是空字典代表不是第一次进来
                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']
        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] = {}
            print("%s 上传完毕!" %fileName)

if __name__=='__main__':
    selectFtpServer()              #第一步:实例化触发selectFtpServer这个类

客户端代码:目录结果:/client/clenb.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=("192.168.1.160",8080)               #如果没有第二个参数默认取这个
        self.create_socket()                             #
        self.command_fanout()                            #进行命令分发

    #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:
            cmd = input('>>>').strip()   #引导用户输入上传还是下载
            if cmd == 'exit()':
                break
            cmd,file = cmd.split()        #把输入的命令分开进行反射
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(cmd,file)
            else:
                print('调用错误!')

    #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,file):
        pass

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