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

时间:2022-05-16 00:00:19
  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()

上面为客户端代码