功能:
1、使用SELECT或SELECTORS模块实现并发简单版FTP
2、允许多用户并发上传下载文件
环境:
python 3.5
特性:
select 实现并发效果
运行:
get 文件名 #从服务器下载文件
put 文件名 #向服务器上传文件
helps #帮助信息
其他命令 #变大写返回给客户端
主要知识点:
os模块的应用
json模块的运用
select模块的运用
socket通信
queue数据交互
粘包
原理:
这个程序通过select实现了并发,主要原理为它通过一个select()系统调用来监视多个文件描述符的数组(在linux中一切事物皆文
件,块设备,socket连接等。),当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位(变成ready),使得进程
可以获得这些文件描述符从而进行后续的读写操作(select会不断监视网络接口的某个目录下有多少文件描述符变成ready状态【在
网络接口中,过来一个连接就会建立一个'文件'】,变成ready状态后,select就可以操作这个文件描述符了)。
通过不同链接的交替,实现并发效果。
主要代码:
readable, writeable, exeptional = select.select(inputs,outputs,inputs) #如果没有任何fd就绪,那程序就会一直阻塞在这里 # select中第1个参数表示inputs中发生变化的句柄放入readable。 # select中第2个参数表示outputs中的值原封不动的传递给writeable。 # select中第3个参数表示inputs中发生错误的句柄放入exeptional.
1 import json 2 import select 3 import socket 4 import queue 5 import os 6 7 8 os.chdir(os.pardir) 9 server = socket.socket() 10 server_addr = ("localhost",1000) 11 server.bind(server_addr) 12 server.listen(7) 13 inputs = [server,] 14 outputs = [] 15 message_queue ={} 16 17 while True: 18 readable,writeable,exeptional = select.select(inputs,outputs,inputs) 19 # 20 # 存放所有的活动 21 for sock in readable: 22 if sock is server: 23 conn, client_addr = sock.accept() 24 inputs.append(conn) 25 message_queue[conn] = queue.Queue() #为防阻塞,先把信息存入队列 26 else: 27 data = sock.recv(1024) 28 if data: 29 message_queue[sock].put(data) 30 if sock not in outputs: 31 outputs.append(sock) 32 33 else: 34 if sock in outputs: 35 outputs.remove(sock) 36 inputs.remove(sock) 37 del message_queue[sock] 38 # 39 # 存放连接信息 40 # 41 for sock in writeable: 42 try: 43 cmd = message_queue[sock].get_nowait() 44 except queue.Empty: 45 outputs.remove(sock) 46 else: 47 print("recv data:", cmd) 48 data = json.loads(cmd.decode()) 49 if data.get('action') is not None: 50 # 51 # 上传文件 52 if data['action'] == 'put': 53 # client sends file to server 54 file_obj = open('data/'+data['filename'], 'wb') 55 received_size = 0 56 sock.send(b'1') 57 while True: 58 if received_size == data['size']: 59 break 60 recv_data = sock.recv(1024) 61 file_obj.write(recv_data) 62 received_size += len(recv_data) 63 if received_size == data['size']: 64 print('Successfully received file ', data['filename']) 65 file_obj.close() 66 # 67 # 下载文件 68 elif data['action'] == 'get': 69 if os.path.isfile(data['filename']): 70 data['file_size'] = os.path.getsize(data['filename']) 71 data['ERROR'] = '0' 72 else: 73 data['ERROR'] = '777' #ERROR标识 74 sock.send(json.dumps(data).encode()) 75 if os.path.isfile(data['filename']): 76 if sock.recv(1) == b'1': #等待客户端响应,防粘包 77 if data['ERROR'] == '0': 78 file_obj = open(data['filename'], 'rb') 79 for line in file_obj: 80 sock.send(line) 81 # 82 # 其他命令 83 else: 84 data['cmd'] = data['cmd'].upper() 85 sock.send(json.dumps(data).encode()) 86 87 # 88 # 错误链接 89 for sock in exeptional: 90 if sock in outputs: 91 outputs.remove(sock) 92 inputs.remove(sock) 93 del message_queue[sock]
1 import json 2 import socket 3 import os 4 5 os.chdir(os.pardir) 6 server_address = ('localhost', 1000) 7 sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 8 sock.connect(server_address) 9 print('''------Welcome!!----- 10 (helps:帮助信息)''') 11 while True: 12 cmd = input(">>>").strip() 13 cmd_list = cmd.split() 14 # 15 #上传文件 16 if cmd_list[0] == 'put': 17 if len(cmd_list) == 1: #命令不能识别 18 print('No filename follows after put cmd!') 19 continue 20 filename = 'data/'+cmd_list[1] #将所有的文件都归入data文件夹 21 if os.path.isfile(filename): 22 file_obj = open(filename,'rb') 23 base_filename = filename.split('/')[-1] 24 data_header = { 25 'action':'put', 26 'filename':base_filename, 27 'size':os.path.getsize(filename) 28 } 29 sock.send(json.dumps(data_header).encode()) 30 if sock.recv(1) == b'1': #防止粘包 31 for line in file_obj: 32 sock.send(line) 33 file_obj.close() 34 print('put all file..') 35 else: 36 print('File is not valid') 37 continue 38 # 39 # 下载文件 40 elif cmd_list[0] == 'get': 41 if len(cmd_list) == 1: 42 print('No filename follows after get cmd!') 43 continue 44 filename = 'data/'+ cmd_list[1] 45 data_header = { 46 'action':'get', 47 'filename':filename, 48 'ERROR':'0' 49 } 50 sock.send(json.dumps(data_header).encode()) 51 data_header = json.loads(sock.recv(1024).decode()) 52 if data_header['ERROR'] == '0': 53 file_obj = open(data_header['filename'], 'wb') 54 received_size = 0 55 sock.send(b'1') 56 while True: 57 58 if received_size == data_header['file_size']: 59 break 60 recv_data = sock.recv(1024) 61 file_obj.write(recv_data) 62 received_size += len(recv_data) 63 64 print('Successfully received file ', data_header['filename']) 65 file_obj.close() 66 # 67 # 帮助信息 68 elif cmd_list[0] == 'helps': 69 print(''' 70 put 文件名 #上传文件 71 get 文件名 #下载文件 72 其他命令 #返回大写''') 73 # 74 # 其他命令 75 else: 76 data_header = { 77 'action':'else', 78 'cmd':cmd 79 } 80 sock.send(json.dumps(data_header).encode()) 81 data_recv = json.loads(sock.recv(1024).decode()) 82 print(data_recv['cmd'])