基于线程开发一个FTP服务器,要点分析
用Python开发一个基于线程的FTP服务器,主要功能如下:
- 1.用户加密认证- 2.允许同时多用户登录- 3.每个用户有自己的家目录 ,且只能访问自己的家目录- 4.对用户进行磁盘配额,每个用户的可用空间不同- 5.允许用户在ftp server上随意切换目录- 6.允许用户查看当前目录下文件- 7.允许上传和下载文件,保证文件一致性(md5)- 8.文件传输过程中显示进度条- 9.附加功能:支持文件的断点续传-
10.在之前开发的FTP基础上,开发支持多并发的功能 - 11.不能使用SocketServer模块,必须自己实现多线程 - 12.必须用到队列Queue模块,实现线程池 - 13.允许配置最大并发数,比如允许只有10个并发用户1,用户加密认证
|
1
2
3
4
|
这个肯定需要用到configparser 和hashlib模块,用md5进行加密,服务端与用户端进行交互前,肯定需要进行认证,在服务端进行认证,客户端需要发送用户名及密码,但是为了安全起见,服务端数据库中的密码应该是加密后的密文,客户端登陆认证时也应该发送密文到服务端,服务端接受到密文与数据库中对应的密文进行比较。 |
2,查看自己的当前目录下的文件
|
1
2
|
这个只需要写一个dir就ok简单的说,使用configparse模块就可以完成 |
3,文件传输中显示进度条
|
1
2
3
|
下载的进度条比较好实现,我们可以从服务端受到将要下载的文件的大小,上传的进度条,我们可以利用文件操作的tell()方法,获取当前指针位置(字节) |
4,在之前的基础上实现多并发的功能
|
1
2
3
4
5
6
|
并发,是伪并行,,即看起来是同时运行的,单个CPU+多道技术就可以实现并发 多道技术概念回顾:内存中同时存入多道(多个)程序,cpu从一个进程快速切换到另外一个,使每个进程各自运行几十或几百毫秒,这样,虽然在某一个瞬间,一个cpu只能执行一个任务,但在1秒内,cpu却可以运行多个进程,这就给人产生了并行的错觉,即伪并发,以此来区分多处理器操作系统的真正硬件并行(多个cpu共享同一个物理内存) |
5,不能使用SocketServer模块,必须自己实现多线程

1、服务器启动socket监听端口
2、服务器内部利用while循环监视句柄的变化
3、客户端请求
4、服务器为这个请求分配线程或进程(底层调用select)。
SocketServer模块有两个方法ThreadingTCPServer和ForkingTCPServer,分别创建线程或者进程。
ThreadingTCPServer源码分析(ForkingTCPServer方法类似)
类继承关系图
服务器启动程序后:
1、执行 TCPServer.__init__ 方法,创建服务端Socket对象并绑定 IP 和 端口
解释:class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass,ThreadingTCPServer先要执行__init__构造函数,ThreadingMixIn里没有,就去TCPServer类里去找
2、执行 BaseServer.__init__ 方法,将自定义的继承自SocketServer.BaseRequestHandler 的类 MyRequestHandle赋值给self.RequestHandlerClass
解释:TCPServer构造方法中包含BaseServer.__init__(self, server_address, RequestHandlerClass),所以要把自己定义的类传给BaseServer的构造方法
3、执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
解释:serve_forever是BaseServer类中的方法,里面有个while循环,一直调用select.select(),r, w, e =_eintr_retry(select.select, [self], [], [],poll_interval)
客户端接入:
4、执行BaseServer._handle_request_noblock方法
解释:serve_forever的while循环里有一个判断if self in r:self._handle_request_noblock(),客户端连接句柄发生变化就会把句柄放到r列表里,所以,触发了_handle_request_noblock()。
5、执行 ThreadingMixIn.process_request 方法,创建一个 “线程” 用来处理请求
解释:调用process_request方法时,从继承类广度优先原则,所以它先调用ThreadingMixIn类中的process_request
6、执行 ThreadingMixIn.process_request_thread 方法
解释:t = threading.Thread(target = self.process_request_thread,args = (request, client_address))多线程模块方法,调用self.process_request_thread,此时才真正启动了线程。
7、执行 BaseServer.finish_request 方法,执行 self.RequestHandlerClass() 即:执行 自定义 MyRequestHandler 的构造方法(自动调用基类BaseRequestHandler的构造方法,在该构造方法中又会调用自定义的MyRequestHandler的handle方法)
解释:连接创建完成,此时开始执行handle方法中的内容,开始和客户端交互,执行完,后面再执行shutdown_request方法关闭连接。
源码精简
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import socketimport threadingimport selectdef process(request, client_address): print request,client_address conn = request conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.') flag = True while flag: data = conn.recv(1024) if data == 'exit': flag = False elif data == '0': conn.sendall('通过可能会被录音.balabala一大推') else: conn.sendall('请重新输入.')sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sk.bind(('127.0.0.1',8002))sk.listen(5)while True: r, w, e = select.select([sk,],[],[],1) print 'looping' if sk in r: print 'get request' request, client_address = sk.accept() t = threading.Thread(target=process, args=(request, client_address)) t.daemon = False t.start()sk.close() |
6,必须用到队列Queue模块,实现线程池
Python标准模块-concurrent.futures:https://docs.python.org/dev/library/concurrent.futures.html
1 -1介绍
|
1
2
3
4
5
6
7
|
concurrent.futures模块提供了高度封装的异步调用接口ThreadPoolExecutor:线程池,提供异步调用ProcessPoolExecutor: 进程池,提供异步调用Both implement the same interface, which is defined by the abstract Executor class.
1-2 基本方法
|
2,进程池
介绍:
|
1
2
3
4
5
6
7
|
The ProcessPoolExecutor class is an Executor subclass that uses a pool ofprocesses to execute calls asynchronously. ProcessPoolExecutor uses themultiprocessing module, which allows it to side-step the Global Interpreter Lock but also means that only picklable objects can be executed and returned.class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None)An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes. If max_workers is None or not given, it will default to the number of processors on the machine. If max_workers is lower or equal to 0, then a ValueError will be raised. |
用法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutorimport os,time,randomdef task(n): print('%s is runing' %os.getpid()) time.sleep(random.randint(1,3)) return n**2if __name__ == '__main__': executor=ProcessPoolExecutor(max_workers=3) futures=[] for i in range(11): future=executor.submit(task,i) futures.append(future) executor.shutdown(True) print('+++>') for future in futures: print(future.result()) |
3,线程池
介绍:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
ThreadPoolExecutor is an Executor subclass that uses a pool of threads to execute calls asynchronously.class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='')An Executor subclass that uses a pool of at most max_workers threads toexecute calls asynchronously.Changed in version 3.5: If max_workers is None or not given, it will defaultto the number of processors on the machine, multiplied by 5, assuming thatThreadPoolExecutor is often used to overlap I/O instead of CPU work and the number of workers should be higher than the number of workers for ProcessPoolExecutor.New in version 3.6: The thread_name_prefix argument was added to allow users to control the threading.Thread names for worker threads created by the pool for easier debugging. |
用法:
|
1
|
把ProcessPoolExecutor换成ThreadPoolExecutor,其余用法全部相同 |

浙公网安备 33010602011771号