1 import os 2 import time 3 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 import socket 5 import selectors 6 7 class selectFtpServer:#服务端创建一个类 8 9 def __init__(self):#对象初始化的定义 10 self.dic = {}#定义一个空字典 11 self.hasReceived=0#定义一个接受值的大小 12 self.sel = selectors.DefaultSelector()#实例化一个selectors对象 13 self.create_socket()#调用建立连接函数 14 self.handle()#调用handle函数。 15 16 def create_socket(self): 17 server = socket.socket() 18 server.bind(("127.0.0.1",8885)) 19 server.listen(5) 20 server.setblocking(False)#非IO阻塞模型 21 self.sel.register(server, selectors.EVENT_READ, self.accept) 22 #注册一个监听对象,如果server变化执行accept函数。 23 print("服务端已开启,等待用户连接...") 24 25 def handle(self): 26 while True: 27 events = self.sel.select()#监听,将变化的内容赋值 28 for key, mask in events:#循环发生变化的对象 29 callback = key.data#将该对象的方法赋值 30 callback(key.fileobj, mask)#执行这个方法并传参 31 32 def accept(self,sock, mask): 33 conn, addr = sock.accept() 34 print("from %s %s connected"%addr) 35 self.sel.register(conn, selectors.EVENT_READ, self.read) 36 #注册conn,如果发生变化执行read函数 37 self.dic[conn] = {}#将conn传入字典,将一个空字典作为值 38 39 def read(self, conn, mask):#read函数具体执行上传和下载的功能 40 try: 41 if not self.dic[conn] :#如果这个conn不在字典里,说明这是第一次接收到该客户端消息 42 data = conn.recv(1024)#接收1024的内容 43 cmd,filename,filesize = str(data, encoding='utf-8').split('|')#将内容以管道符分割,分别赋值 44 self.dic={conn:{"cmd": cmd, "filename": filename,"filesize": int(filesize)}}#将取到的内容放到字典里 45 if cmd == 'put':#如果cmd为put 46 conn.send(bytes("OK",encoding='utf8'))#回一个返回值 47 if self.dic[conn]['cmd'] == 'get':#如果传来的命令是get 48 file = os.path.join(BASE_DIR,"download",filename)#拼接文件路径 49 if os.path.exists(file):#如果文件存在 50 fileSize = os.path.getsize(file)#文件大小等于filesize 51 send_info = '%s|%s'%('YES',fileSize)#将文件大小等放入事先定制好的格式当中。 52 conn.send(bytes(send_info, encoding='utf8'))#发送这个文件信息 53 else: 54 send_info = '%s|%s'%('NO',0)#如果文件不存在,将消息赋值 55 conn.send(bytes(send_info, encoding='utf8'))#发送 56 else: 57 if self.dic[conn].get('cmd',None):#如果conn在字典里,取出cmd,如果没有则返回None 58 cmd=self.dic[conn].get('cmd')#将get到的内容赋值给cmd 59 if hasattr(self, cmd):#如果cmd这个函数存在 60 func = getattr(self,cmd)#获取这个函数并赋值 61 func(conn)#加上参数,调用执行 62 else: 63 print("error cmd!")#如果这个函数不存在,打印error 64 conn.close()#关闭连接 65 else: 66 print("error cmd!") 67 conn.close() 68 69 except Exception as e:#异常处理 70 print('error', e) 71 self.sel.unregister(conn)#解除注册 72 conn.close() 73 74 def put(self, conn):#上传 75 76 fileName = self.dic[conn]['filename']#字典的文件名赋值 77 fileSize = self.dic[conn]['filesize']#字典的文件大小赋值 78 path = os.path.join(BASE_DIR,"upload",fileName)#文件拼接 79 recv_data = conn.recv(1024)#收1024 80 self.hasReceived += len(recv_data)#已经收到的 81 82 with open(path, 'ab') as f:#做收文件内容的操作 83 f.write(recv_data) 84 if fileSize == self.hasReceived:#如果收到的大小等于实际大小 85 if conn in self.dic.keys():#如果这个conn在字典中存在则清空 86 self.dic[conn] = {} 87 print("%s上传完毕!"%fileName) 88 89 def get(self,conn):#下载 90 filename = self.dic[conn]['filename']#取文件名 91 path = os.path.join(BASE_DIR,"download",filename)#拼接路径 92 if str(conn.recv(1024), 'utf-8') == "second_active":#收文件 93 with open(path, 'rb') as f: 94 for line in f: 95 conn.send(line) 96 self.dic[conn] = {}#请空字典 97 print('文件下载完毕!') 98 99 100 if __name__ == '__main__': 101 102 selectFtpServer()
上面是server端代码,主要就是通过socket和selectors来实现,可以多加练习。
1 import socket 2 import os,sys 3 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 5 class selectFtpClient: 6 7 def __init__(self): 8 self.args=sys.argv 9 if len(self.args)>1: 10 self.port=(self.args[1],int(self.args[2]))#如果运行的时候后面有参数 11 else: 12 self.port=("127.0.0.1",8885)#默认 13 self.create_socket()#创建连接函数 14 self.command_fanout()#通过反射获取函数的函数 15 16 def create_socket(self): 17 try: 18 self.sk = socket.socket() 19 self.sk.connect(self.port) 20 print('连接FTP服务器成功!') 21 except Exception as e: 22 print("error: ",e) 23 24 def command_fanout(self): 25 while True: 26 cmd = input('>>>').strip() 27 if cmd == 'exit()': 28 break 29 cmd,file = cmd.split() 30 if hasattr(self, cmd): 31 func = getattr(self, cmd) 32 func(cmd,file) 33 else: 34 print('调用错误!') 35 36 def put(self,cmd,file): 37 38 if os.path.isfile(file): 39 fileName= os.path.basename(file) 40 fileSize = os.path.getsize(file) 41 fileInfo ='%s|%s|%s'%(cmd,fileName,fileSize) 42 self.sk.send(bytes(fileInfo, encoding='utf8')) 43 recvStatus = self.sk.recv(1024) 44 print('recvStatus', recvStatus) 45 hasSend = 0 46 if str(recvStatus, encoding='utf8') == "OK": 47 with open(file, 'rb') as f: 48 while fileSize > hasSend : 49 contant = f.read(1024) 50 recv_size = len(contant) 51 self.sk.send(contant) 52 hasSend += recv_size 53 s=str(int(hasSend/fileSize*100))+"%" 54 print("正在上传文件:"+fileName+" 已经上传:"+s) 55 print('%s文件上传完毕' % (fileName,)) 56 else: 57 print('文件不存在') 58 59 60 if __name__ == '__main__': 61 62 selectFtpClient()
上面为客户端代码