python TCPServer, StreamRequestHandler设置超时时间timeout

来源:互联网 发布:centos查看文件夹大小 编辑:程序博客网 时间:2024/06/08 12:30

先来大概说一下,网络编程中总是分不开服务器和客户端,所谓的超时也分两种情况,一种是服务器等待客户端连接超时,一种是服务器处理客户端请求超时(可以理解为sever和handle),这两种情况要分别对待。

先来看看等待连接超时的server timeout

python对服务器也有简单的封装,先看看文档。

        +------------+        | BaseServer |        +------------+              |              v        +-----------+        +------------------+        | TCPServer |------->| UnixStreamServer |        +-----------+        +------------------+              |              v        +-----------+        +--------------------+        | UDPServer |------->| UnixDatagramServer |        +-----------+        +--------------------+
这就是基础服务器的继承关系,当然除此之外还有HTTP的服务器HTTPServer继承自TCPServer等。

在基类BaseServer中有一个timeout变量。BaseServer中有一个handle_request函数,源码如下:

    def handle_request(self):        """Handle one request, possibly blocking.        Respects self.timeout.        """        # Support people who used socket.settimeout() to escape        # handle_request before self.timeout was available.        timeout = self.socket.gettimeout()        if timeout is None:            timeout = self.timeout        elif self.timeout is not None:            timeout = min(timeout, self.timeout)        fd_sets = _eintr_retry(select.select, [self], [], [], timeout)        if not fd_sets[0]:            self.handle_timeout()            return        self._handle_request_noblock()
可以看到,这里设置了timeout并用了select的复用,select的时间间隔是timeout,如果不设置timeout,那就一直等待。

需要说明的一点,一旦创建了socket服务,那么有两种方法可以开启服务,分别是server_forerver()和handle_request(),handle_request的代码如上,那么server_forerver的代码又是怎样的?如下:

    def serve_forever(self, poll_interval=0.5):        """Handle one request at a time until shutdown.        Polls for shutdown every poll_interval seconds. Ignores        self.timeout. If you need to do periodic tasks, do them in        another thread.        """        self.__is_shut_down.clear()        try:            while not self.__shutdown_request:                # XXX: Consider using another file descriptor or                # connecting to the socket to wake this up instead of                # polling. Polling reduces our responsiveness to a                # shutdown request and wastes cpu at all other times.                r, w, e = _eintr_retry(select.select, [self], [], [],                                       poll_interval)                if self in r:                    self._handle_request_noblock()        finally:            self.__shutdown_request = False            self.__is_shut_down.set()
注意,这儿的注释有句话:Polls for shutdown every poll_interval seconds. Ignores  self.timeout.If you need to do periodic tasks,好吧,这里把ignores和timeout分写两行了,实在是不太注意到。

同样,server_forerver里面同样也用了select多路复用,这里的时间间隔是poll_interval,默认是0.5s,但是对比handle_request函数,server_forerver函数中并没有对handle_timeout,而server_request函数中有两行

if not fd_sets[0]:
            self.handle_timeout()
            return

在这个地方进行超时处理,这个地方可以对基类的handle_timeout进行覆盖,做我们想做的事,说明的是pyhton中所有的方法默认都是虚函数,所以只要覆盖了就会生效。

来简单梳理一下。当TCPServer创建好之后,有两种开启方式,server_forerver和handle_request,这两种的区别就是server_forever不会处理超时,而handle_request会处理超时。

再来看一下处理超时 handle_timeout

同样的,python对请求的处理也有相关的类,BaseRequestHandler、StreamRequestHandler、DatagramRequestHandler,注意的是BaseRequestHandler中并没有timeout,StreamRequestHandler才有。注释说是 # A timeout to apply to the request socket, if not None.所以这里指的是客户端从发起请求到请求结束超时,假如有恶意的连接攻击,可以设置超时,断开连接。

最后再来整理一下。超时有两种,一种是等待连接超时,一种是处理请求超时,这两种的超时在两个类中

示例代码:

service:

#!/usr/bin/pythonimport socketimport timefrom SocketServer import TCPServer, StreamRequestHandler__author__ = 'meiqizhang'class MyStreamRequestHandlerr(StreamRequestHandler):    def __init__(self, request, client_address, server):        print('%s create handler...' % (time.time()))        StreamRequestHandler.__init__(self, request, client_address, server)    def handle(self):        self.request.settimeout(3)        try:            data = self.rfile.readline().strip()            time.sleep(5)            print('%s recv from client: %s %s' % (time.time(), self.client_address, data))        except socket.timeout as e:            print('%s catch an timtout exception. %s(%s)' % (time.time(), e, self.client_address))            self.finish()class SimpleServer(TCPServer):    #timeout = 2    def __init__(self, server_address, RequestHandlerClass):        #self.timeout = 2        TCPServer.__init__(self, server_address, RequestHandlerClass)    def handle_timeout(self):        print('%s timeout...' % time.time())def main():    server = SimpleServer(('127.0.0.1', 888), MyStreamRequestHandlerr)    server.timeout = 2    print('server running...')    #server.serve_forever()    while True:        server.handle_request()if '__main__' == __name__:    main()

对于连接超时的设置三种方法都可以,可以直接写timeout = 2,也可以self.timeout = 2,还可以server.timeout = 2,而对于处理请求的超时的设置,要在handle中通过self.request.settimeout(3)来设置。

client:

#!/usr/local/bin/python#coding=utf-8import timeimport socketsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.connect(('localhost', 888))print("connect server success!")data = 'hello world'sock.send(data)time.sleep(10)data = sock.recv(1023)print(data)sock.close()
先运行server,因为没有连接,每隔2秒会打印出timeout。接着运行client,在收到请求后handler中暂停了5秒,超过了处理请求超时时间3秒,打印出catch an timtout exception. timed out。


最后说一点:行TCPServer收到客户端的accept连接请求,就会创建一个handler对象对其进行请求处理(会把accept返回是socket传递到handler)

0 0
原创粉丝点击