这是Python Cookbook中的一个例子
服务器等待客户端连接的代码
from threading import Thread from socket import socket, AF_INET, SOCK_STREAM def echo_server(addr, nworkers): # Run the server sock = socket(AF_INET, SOCK_STREAM) sock.bind(addr) # 这里设置了listen(5),listen(5)限制了一个时刻服务器最多接收的客户端。 sock.listen(5) while True: client_sock, client_addr = sock.accept() # 调用echo_client来处理客户端的连接 t = Thread(target=echo_client, args=(client_sock, client_addr)) t.daemon = True t.start() echo_server(('',15000)
服务器处理客户端的代码
def echo_client(sock, client_addr): ''' Handle a client connection ''' print('Got connection from', client_addr) while True: msg = sock.recv(65536) if not msg: break sock.sendall(msg) print('Client closed connection') sock.close()
问题一
书中是这样描述上面这个例子的: 尽管这个也可以工作, 但是它不能抵御有人试图通过创建大量线程让你服务器资源枯竭而崩溃的攻击行为。
但是我不明白的是服务器已经设置了listen(5),这个listen(5)难道不会限制每次只有5个客户端能够连接吗?攻击者怎么可以创造多个线程?
问题二
对于服务器网络IO延迟有一点不懂的地方,以上面的代码段2为例子,网络IO延迟是不是就是msg = sock.recv(65536)和sock.sendall(msg)中由于网速原因造成的延迟?
问题一
bacuklog的定义
这个参数涉及到一些网络的细节。在进程正理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。
进一步解释
当客户端请求到来时,服务器会处理他们的请求,在你的代码中就是创建一个线程来处理。那么请求多了之后就会创建很多个线程,这样服务器资源总会到达上限。
backlog为5,实际上不是限制处理的线程为5,而是当服务器忙不过来了,内核会hold住一个长度为5的队列,一旦服务器忙过来了,就会从这个队列中拿出一个来处理,直到队列为空。
问题二
网路延迟比较好理解,以下的过程都和网络有关:
客户端和服务端建立连接
客户端发送数据
服务器端返回数据
断开连接
listen表示在拒绝新的连接之前,系统可以挂起的最大连接数量,这些连接还没有被accept方法接收到,可以看源代码的解释,如下
而已经接受到的连接可能有很多,它们创建了很多很多的线程。假如echo_client处理比较耗时。越来越多的线程进来,最后可能就会导致系统内存过载,最后资源不足。