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前端了,激动。。。