功能:
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]
server端