Python学习第58天(selector版本的ftp习题实现)

  本来打算开始web前端的学习的,但是布置的习题还是应该做一下的,花了不少时间,貌似还有一个功能没有完成,但是重点已经有了,其实先来回顾一下昨天引入的这个selector模块吧,说说里面的重点。  

import selectors
import socket

sel = selectors.DefaultSelector()

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

def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()

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

  这里应该注意的句式,就是

    sel = selectors.DefaultSelector()

    sel.register(sock, selectors.EVENT_READ, accept)

  这两句就是绑定了监测对象,sock发生变化时候,就会执行accept

   大致就是个这么个意思,监测的对象可以是events是个对象列表,不止一个会发生变化,所以要利用for循环遍历,保证每次都能将每个有变化的conn接收到信息

  然后后面的习题就是通过这个特性实现的

  sel.register(sock, selectors.EVENT_READ, accept)把sock加入了监测列表,accept函数又把conn加入了监测列表

  好了,知识会议的差不多了,上干货,干了三个小时的杰作:

  客户端:

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

class selectFtpClent:

    def __init__(self):
        self.args = sys.argv
        if len(self.args) > 1:
            self.port = (self.args[1],int(self.args[2]))
        else:
            self.port = ('127.0.0.1', 8080)
        self.creat_socket()
        self.command_fanout()

    def creat_socket(self):
        try:
            self.sk = socket.socket()
            self.sk.connect(self.port)
            print('连接FTP服务器成功')
        except Exception as e:
            print('error: ',e)


    def command_fanout(self):
        while True:
            cmd = input('输入命令>>>')
            if cmd == 'exit()':
                break
            cmd,file = cmd.split()
            if hasattr(self, cmd):
                func = getattr(self,cmd)
                func(cmd,file)
            else:
                print('命令错误')


    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)
            self.sk.send(bytes(fileInfo,encoding = 'utf-8'))
            recvStatus = self.sk.recv(1024)
            print('recvStatus: ', recvStatus)

            has_send = 0
            if str(recvStatus, encoding = ' utf-8') == 'OK':
                with open(file, 'rb') as f:
                    while fileSize > has_send:
                        contant = f.read(1024)
                        recv_size = len(contant)
                        self.sk.send(contant)
                        has_send +=recv_size
                        s = str(int(has_send/fileSize)) + '%'
                        print('正在上传文件:'+ fileName + '已上传' + s)
                print('%s文件上传完毕%'%fileName)
            else:
                print('文件不存在')

    def get(self):
        pass

if __name__ == '__main__':
    selectFtpClent()

  服务端:

import os
import time
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
import socket
import selectors

class selectFtpServer:

    def __init__(self):
        self.dic = {}
        self.hasReceived = 0
        self.sel = selectors.DefaultSelector()
        self.creat_socket()
        self.handle()

    def creat_socket(self):
        server = socket.socket()
        server.bind(('127.0.0.1' , 8080))
        server.listen(5)
        server.setblocking(False)
        self.sel.register(server, selectors.EVENT_READ, self.accept)
        print('服务器已开启,等待用户连接......')

    def handle(self):
        while True:
            events = self.sel.select()
            for key,mask in events:
                callback = key.data
                callback(key.fileobj, mask)

    def accept(self,sock,mask):

        conn, addr = sock.accept()
        print('from %s %s connected'%addr)
        conn.setblocking(False)
        # 作为后面conn的标记,防止很多conn的一个标记,同时记录相关信息
        self.dic[conn] = {}

    def read(self,conn,mask):
        try:
            if not self.dic[conn]:
                data = conn.recv(1024)
                cmd,filename,filesize = str(data,encoding = 'utf-8').split('|')
                self.dic = {conn : { 'cmd':cmd , 'filename':filename ,'filesize':int(filesize)} }

                if cmd == 'put':
                    conn.send(bytes('OK',encoding = 'utf-8'))

                if self.dic[conn]['cmd'] == 'get':
                    file = os.path.join(BASE_DIR, 'download', filename)

                    if os.path.exists(file):
                        fileSize = os.path.getsize(file)
                        send_info = '%s|%s'%('YES',fileSize)
                        conn.send(bytes(send_info,encoding = 'utf-8'))
                    else:
                        send_info = '%s|%s'%('NO',0)
                        conn.send(bytes(send_info, encoding='utf-8'))

            else:
                if self.dic[conn].get('cmd',None):
                    cmd = self.dic[conn].get('cmd')
                    if hasattr(self,cmd):
                        func = getattr(self,cmd)
                        func(conn)
                    else:
                        print('error cmd!!!')
                        conn.close()
                else:
                    print('error cmd')
                    conn.close()
        except Exception as e:
            print('error: ',e)




    def put(self,conn):

        file_name = self.dic[conn]['filename']
        file_size = self.dic[conn]['filesize']
        path = os.path.join(BASE_DIR,'upload',file_name)
        recv_data = conn.recv(1024)
        self.hasReceived += len(recv_data)

        with open(path,'ab') as f:
            f.write(recv_data)
        if file_size == self.hasReceived:
            if conn in self.dic.keys():
                self.dic[conn] = {}
            print('%s上传完毕!!!'%file_name)

    def get(self,conn):
        pass

if __name__ == '__main__':
    selectFtpServer()

  虽然get方面目前还是pass,但是这个都是简单模式,关键是搞定了多客户的同时传递,避免socketserver模块一次存在的一些弊端,比如多用户分别占用的问题

  嗯,明天就要开始web前端了,激动。。。

猜你喜欢

转载自www.cnblogs.com/xiaoyaotx/p/12741611.html