通过非IO阻塞模型实现ftp并发的小代码

  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()

上面为客户端代码

原文地址:https://www.cnblogs.com/ArmoredTitan/p/7250747.html