bottle框架剖析

bottle框架剖析

 

  1. 使用

  2. 源碼分析

 

1、使用

大體有如下幾部分javascript

  1. quick start

  2. request routing

  3. generate contents

  4. request Data

  5. templates

 

quick start

下載: pip install bottlehtml

 

from bottle import route, run

@route('/hello')
def hello():
    return "Hello World!"

run(host='localhost', port=8080, debug=True)

visit http://localhost:8080/hello       將會看到hello worldjava

 

默認app python

當第一次運行route() 會自動產生一個Bottle類的一個實例,bottleajax

request routingshell

route 裝飾器將url path 與回調函數聯繫起來。並給默認的app 添加一個url.一個app能夠添加好多個url.json

like the followingapi

@route('/')
@route('/hello/<name>')
def greet(name='Stranger'):
    return template('Hello {{name}}, how are you?', name=name)

多個url瀏覽器

from bottle import route, run, template

@route('/')
@route('/hello/<name>')
def index(name='stranger'):
    return template('<b>hello</b>{{name}}',name=name)

run(host='127.0.0.1', port=8080)

 

 1能夠將多個路由綁定到一個回調函數上。安全

 2您能夠添加通配符到URL並經過關鍵字參數訪問它們。

動態路由

url裏面有通配符的叫作動態路由,匹配一個或者多個url 在一樣時間裏。

 A simple wildcard consists of a name enclosed in angle brackets (e.g. <name>) and accepts one or more characters up to the next slash (/).

For example, the route /hello/<name> accepts requests for /hello/alice as well as /hello/bob, but not for /hello, /hello/ or /hello/mr/smith.

 每個通配符都會將URL包含到那部分做爲關鍵字參數傳給請求的回調函數。

HTTP 請求方法

使用route()裝飾器的method關鍵字參數或者是用5種裝飾器 get() post() put() delete() or patch()

 
 
from bottle import route, run, template,get,post,request
@get('/login')
def login():
return '''
<form action="/login" method="post">
Username: <input name="username" type="text" />
Password: <input name="password" type="password" />
<input value="Login" type="submit" />
</form>
'''
@post('/login')
def do_login():
username = request.forms.get('username')
password = request.forms.get('password')
if username == 'yuyang' and password == '123':
return "<p>Your login information was correct.</p>"
else:
return "<p>Login failed.</p>"
run(host='127.0.0.1', port=8080)
 

 

reponse object

Response 的元數據好比狀態嗎,響應頭,cookie 都存儲在response 對象裏。

Status Code

默認200 ok ,一般不須要手動設置

Response Header

Response headers such as Cache-Control or Location are defined via Response.set_header(). This method takes two parameters, a header name and a value. The name part is case-insensitive:

響應頭。好比Cache-Control or Location 經過Response.set_header() 定義。這個方法須要兩個參數,一個是頭名字,一個是值,名稱部分不區分大小寫。

 

大部分的頭是獨一無二的。意味着只有一個標頭會被髮給客戶端,有些特別的頭容許在相應出現不止一次,爲了添加額外的標頭,使用Response.add_header() 去代替Response.set_header()

response.set_header('Set-Cookie', 'name=value')
response.add_header('Set-Cookie', 'name2=value2')

 

這只是個例子,若是想使用cookie. 往下看。

Cookies

訪問以前定義的cookie 能夠經過 Request.get_cookie(),設置新的cookies 用Response.set_cookie()

 

@route('/hello')
def hello_again():
    if request.get_cookie("visited"):
        return "Welcome back! Nice to see you again"
    else:
        response.set_cookie("visited", "yes")
        return "Hello there! Nice to meet you"

 

 

Response.set_cookie()還接受一系列的關鍵字參數

  • max_age: Maximum age in seconds. (default: None)    存在時間
  • expires: A datetime object or UNIX timestamp. (default: None)   到期時間,datetime 對象或者unix timestamp
  • domain: The domain that is allowed to read the cookie. (default: current domain)    
  • path: Limit the cookie to a given path (default: /)   限制cookie 取得路徑
  • secure: Limit the cookie to HTTPS connections (default: off).
  • httponly: Prevent client-side javascript to read this cookie (default: off, requires Python 2.7 or newer).
  • same_site: Disables third-party use for a cookie. Allowed attributes: lax and strict.
  • In strict mode the cookie will never be sent. In lax mode the cookie is only sent with a top-level GET request.

    使用cookie 注意的幾點

  • cookie 被限制在4KB之內。
  • 有時候用戶會選擇將cookie 服務關掉,確保你的應用在沒喲cookie狀況下仍然可以使用
  • cookie 被存儲在客戶端,而且沒有被加密,用戶可以讀取它。更糟糕的是,攻擊者可能偷掉用戶的cookies,經過xss 攻擊,
  • 一些病i毒也知道讀取瀏覽器的cookie,,所以永遠不要吧機密信息放在cookie中。
  • cookie很容易被篡改。不要信任cookie

 

signed cookie

 

cookies 能夠輕易的被惡意的客戶端篡改。Bottle可以經過加密技術簽名你的cookie,來防止被篡改。

你所須要作的是提供一個key 用過secret 關鍵字參數傳進去。不管何時去讀或者設置cookie 保持這個key 是祕密的。若是cookie 沒有簽名或者不匹配,Resuqst.get_cookie將返回None

 

 回到頂部

REQUEST DATA

request object 是 BaseRequest 的子類。有豐富的藉口去訪問數據。

FormDic

使用一種特別的字典類型去存儲data 和cookies ,FormDict 的行爲像是一個正常的字典,可是額外的特點讓你生活更簡單,輕鬆。

屬性訪問

多值一個key    getall()

FormsDict is a subclass of MultiDict and can store more than one value per key.

The standard dictionary access methods will only return a single value, but the getall() method returns a (possibly empty) list of all values for a specific key:

 

 

cookies

全部被客戶端發來的cookies 經過BaseRequest.cookies(a FormDcit)都是可用的

 

 

from bottle import route, request, response
@route('/counter')
def counter():
    count = int( request.cookies.get('counter', '0') )
    count += 1
    response.set_cookie('counter', str(count))
    return 'You visited this page %d times' % count

 

.get_cookie() 能夠解密簽名的cookies

 

HTTP HEADERS

全部從客戶端發的HTTP headers 都被存放在 WSGIHeaderDcit 裏,並能經過BaseRequest.headers 屬性訪問到,WSGIHeaderDcit 是基於key 大小寫不敏感的字典。

 

from bottle import route, request
@route('/is_ajax')
def is_ajax():
    if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
        return 'This is an AJAX request'
    else:
        return 'This is a normal request'

 

Query Variables

經過BaseRequest.query屬性(FormDict)

BaseRequest.query_string 是得到整個字符串。

POST FORM 表單

post data 存放在 BaseRequest.forms

 JSON Content

BaseRequest.json 包含解析後的數據結構。

回到頂部

 


 

源碼分析:

從request 入手

bootle 框架,只有一個bottle.py 文件,

若不指明,如下全部代碼都是bottl.py 文件裏的

bottle.py
request = LocalRequest()

 

 

class LocalRequest(BaseRequest):
    ''' A thread-local subclass of :class:`BaseRequest` with a different
        set of attributes for each thread. There is usually only one global
        instance of this class (:data:`request`). If accessed during a
        request/response cycle, this instance always refers to the *current*
        request (even on a multithreaded server). '''
    bind = BaseRequest.__init__
    environ = local_property()

 

 

def local_property(name=None):
    if name: depr('local_property() is deprecated and will be removed.') #0.12
    ls = threading.local()
    def fget(self):
        try: return ls.var
        except AttributeError:
            raise RuntimeError("Request context not initialized.")
    def fset(self, value): ls.var = value
    def fdel(self): del ls.var
    return property(fget, fset, fdel, 'Thread-local property')

 

在local_prperty() 函數中,定義了三個子函數,並用到了property修飾,使得ls 能夠獲取值,設置值。刪除值,

好比request.environ  #獲取

request.environ= 'asdasds'  #設置

del request.environ  #刪除

 

本身測試

dic={} 沒用

'
>>> class Foo():
    dic={}
    def __init__(self,na):
        self._name = na
    def fget(self):
        return self._name
    def fset(self,value):
        self._name = value
        return value
    def fdel(self):
        del self._name
    name = property(fget,fset,fdel)

    
>>> f1=Foo()
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    f1=Foo()
TypeError: __init__() missing 1 required positional argument: 'na'
>>> f1=Foo('yuyang')
>>> f1.name
'yuyang'
>>> f1.name = 'alex'
>>> f1.name
'alex'
>>> del f1.name
>>> f1.name
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    f1.name
  File "<pyshell#14>", line 6, in fget
    return self._name
AttributeError: 'Foo' object has no attribute '_name'
>>> class Foo():
    dic={}
    def __init__(self,na):
        self._name = na
    def fget(self):
        return self._name
    def fset(self,value):
        self._name = value
        return value
    
    name = property(fget,fset)

    
>>> f1=Foo('yuyang')
>>> f1.name
'yuyang'
>>> f1.name ='da
SyntaxError: EOL while scanning string literal
>>> f1.name ='da'
>>> del f1.name
Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    del f1.name
AttributeError: can't delete attribute
>>> 
View Code

 

property([fget[, fset[, fdel[, doc]]]])

Return a property attribute for new-style classes (classes that derive from object).

fget is a function for getting an attribute value, likewise fset is a function for setting, and fdel a function for del’ing, an attribute. Typical use is to define a managed attribute x:

https://docs.python.org/release/2.6/library/functions.html#property

 

environ = property(fget, fset, fdel, 'Thread-local property')   #用到了閉包函數。

 

ls = threading.local()

 

Thread-local data is data whose values are thread specific. To manage thread-local data, just create an instance of local (or a subclass) and store attributes on it:

mydata = threading.local()
mydata.x = 1

 

The instance’s values will be different for separate threads.

class threading. local

A class that represents thread-local data.

For more details and extensive examples, see the documentation string of the _threading_local module.

 

import time
import threading
from threading import Thread

def func(i):
    mydata.x=i
    print('xiancheng %s mydata.x is %s' %(i,mydata.x))



mydata = threading.local()
mydata.x = 'zhuxiancheng'
ls = []
for i in range(2):
    t = Thread(target=func,args=(i,))
    ls.append(t)
    t.start()

for i in ls:
    i.join()

print(mydata.x)
print('end')
View Code

 

從上面的代碼能夠看出threading.local() 在不一樣的線程的值是不同的。

 總的來講 bottle框架只有一個py文件 當import bottle 時

作了如下幾件事

request = LocalRequest()

#: A thread-safe instance of :class:`LocalResponse`. It is used to change the
#: HTTP response for the *current* request.
response = LocalResponse()

#: A thread-safe namespace. Not used by Bottle.
local = threading.local()

# Initialize app stack (create first empty Bottle app)
# BC: 0.6.4 and needed for run()
app = default_app = AppStack()

app.push()

 

實例化一個request對象,response對象,執行,app = default_app = AppStack()  返回app=AppStack 實例對象  app.push()  會在列表裏添加一個bottle 實例對象。

class AppStack(list):
    """ A stack-like list. Calling it returns the head of the stack. """

    def __call__(self):
        """ Return the current default application. """
        return self[-1]

    def push(self, value=None):
        """ Add a new :class:`Bottle` instance to the stack """
        if not isinstance(value, Bottle):
            value = Bottle()
        self.append(value)
        return value

 

 AppStack 類繼承Python列表

local = threading.local()

 

這是一個線程安全的名稱空間

第二部分   route

first_bottle.py
@route('/hello') def hello(): if request.get_cookie('account'): return 'welcome back! nice to see you again!' else: return 'you are not logged in ,access denied!'

 

源碼:

route     = make_default_app_wrapper('route')



def make_default_app_wrapper(name):
    ''' Return a callable that relays calls to the current default app. '''
    @functools.wraps(getattr(Bottle, name))
    def wrapper(*a, **ka):
        return getattr(app(), name)(*a, **ka)
    return wrapper

 

 

@route('/hello') 是一個有參裝飾器。


關於有參裝飾器

@route('/hello')
def login():
pass
實際會執行如下的代碼:
tmp = route('/hello') #第一步 def login():         #第二步 pass
login = tmp(login)    #第三步

 


固然在import bottle.py 時,就執行了這句代碼:
route     = make_default_app_wrapper('route')

 

因此這時候route 實際是 wrapper 函數。
執行步驟應該是:
tmp = wrapper('/hello')
tmp = getattr(app(), name)(*a, **ka)
app 以前分析是由AppStack類實例出的對象,app() 會調用__call__() 方法,返回self[-1] 實際就是bottle 實例對象。

咱們知道name= 'route'
從bottle對象去反射,找route方法,並調用

讓咱們看下Bottle 類下的route 方法作了什麼
def route(self, path=None, method='GET', callback=None, name=None,
              apply=None, skip=None, **config):
        """ A decorator to bind a function to a request URL. Example::

                @app.route('/hello/:name')
                def hello(name):
                    return 'Hello %s' % name

            The ``:name`` part is a wildcard. See :class:`Router` for syntax
            details.

            :param path: Request path or a list of paths to listen to. If no
              path is specified, it is automatically generated from the
              signature of the function.
            :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
              methods to listen to. (default: `GET`)
            :param callback: An optional shortcut to avoid the decorator
              syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
            :param name: The name for this route. (default: None)
            :param apply: A decorator or plugin or a list of plugins. These are
              applied to the route callback in addition to installed plugins.
            :param skip: A list of plugins, plugin classes or names. Matching
              plugins are not installed to this route. ``True`` skips all.

            Any additional keyword arguments are stored as route-specific
            configuration and passed to plugins (see :meth:`Plugin.apply`).
        """
        if callable(path): path, callback = None, path
        plugins = makelist(apply)
        skiplist = makelist(skip)
        def decorator(callback):
            # TODO: Documentation and tests   
            if isinstance(callback, basestring): callback = load(callback)
            for rule in makelist(path) or yieldroutes(callback):
                for verb in makelist(method):
                    verb = verb.upper()
                    route = Route(self, rule, verb, callback, name=name,
                                  plugins=plugins, skiplist=skiplist, **config)
                    self.add_route(route)
            return callback
        return decorator(callback) if callback else decorator
 

 

分析這段代碼,不可貴知,咱們將'/hello'傳進去,path = '/hello'
先判斷path 是否是可調用對象,好比函數,實例等,若是是的話,callback 和path 的值互換。
makelist() 是吧一個對象放在列表中
def makelist(data): # This is just to handy
    if isinstance(data, (tuple, list, set, dict)): return list(data)
    elif data: return [data]
    else: return []
 

 

這個方法其實也是一個裝飾器。

因此回到最初
getattr(app(), name)(*a, **ka) 這段代碼執行結果會獲得decorator(callback) if callback else decorator

此次咱們並無傳遞callback 進去,因此會獲得decorator

因此tmp = decorator

至此有參裝飾器執行完第一步,

第二步是定義login 函數
第三步
login = decorator(login)

        def decorator(callback):
            # TODO: Documentation and tests   
            if isinstance(callback, basestring): callback = load(callback)
            for rule in makelist(path) or yieldroutes(callback):
                for verb in makelist(method):
                    verb = verb.upper()
                    route = Route(self, rule, verb, callback, name=name,
                                  plugins=plugins, skiplist=skiplist, **config)
                    self.add_route(route)
            return callback
 

 

callback參數對應是login 函數
basestring = str #bottle.py 提早定義的

因此這段代碼執行完,會實例化一個route 對象,並吧route 對象添加到bottle 對象裏。返回callback 函數,因此得出被route裝飾後的login 實際仍是原來的login 函數。

    def add_route(self, route):
        ''' Add a route object, but do not change the :data:`Route.app`
            attribute.'''
        self.routes.append(route)
        self.router.add(route.rule, route.method, route, name=route.name)
 

 

 
class Bottle
self.routes = [] # List of installed :class:`Route` instances. self.router = Router() # Maps requests to :class:`Route` instances.
 

 

有點複雜,先略過

執行路由大體分析完了

如今讓咱們分析最後讓服務器啓動的代碼
first_bottle.py

run(host='127.0.0.1', port=8080,reloader=True)
 

 

 
def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
        interval=1, reloader=False, quiet=False, plugins=None,
        debug=None, **kargs):
    """ Start a server instance. This method blocks until the server terminates.

        :param app: WSGI application or target string supported by
               :func:`load_app`. (default: :func:`default_app`)
        :param server: Server adapter to use. See :data:`server_names` keys
               for valid names or pass a :class:`ServerAdapter` subclass.
               (default: `wsgiref`)
        :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
               all interfaces including the external one. (default: 127.0.0.1)
        :param port: Server port to bind to. Values below 1024 require root
               privileges. (default: 8080)
        :param reloader: Start auto-reloading server? (default: False)
        :param interval: Auto-reloader interval in seconds (default: 1)
        :param quiet: Suppress output to stdout and stderr? (default: False)
        :param options: Options passed to the server adapter.
 

 

reloader 參數表示是否是從新加載,默認是false ,interval 表示隔多長時間加載一次,默認是一秒。
quiet 表示是否是廢止輸出錯誤信息等。默認是輸出錯誤信息等。若是是True.不會再終端打印某些錯誤信息等。
    if reloader and not os.environ.get('BOTTLE_CHILD'):
        try:
            lockfile = None
            fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
            os.close(fd) # We only need this file to exist. We never write to it
            while os.path.exists(lockfile):
                args = [sys.executable] + sys.argv
                environ = os.environ.copy()
                environ['BOTTLE_CHILD'] = 'true'
                environ['BOTTLE_LOCKFILE'] = lockfile
                p = subprocess.Popen(args, env=environ)
                while p.poll() is None: # Busy wait...
                    os.utime(lockfile, None) # I am alive!
                    time.sleep(interval)
                if p.poll() != 3:
                    if os.path.exists(lockfile): os.unlink(lockfile)
                    sys.exit(p.poll())
        except KeyboardInterrupt:
            pass
        finally:
            if os.path.exists(lockfile):
                os.unlink(lockfile)
        return

 

上面這段是當你設置參數從新加載爲True 執行的代碼,暫不分析這段代碼,
如下應該是run 函數的核心代碼。

    try:
        if debug is not None: _debug(debug)
        app = app or default_app()
        if isinstance(app, basestring):
            app = load_app(app)
        if not callable(app):
            raise ValueError("Application is not callable: %r" % app)

        for plugin in plugins or []:
            app.install(plugin)

        if server in server_names:
            server = server_names.get(server)
        if isinstance(server, basestring):
            server = load(server)
        if isinstance(server, type):
            server = server(host=host, port=port, **kargs)
        if not isinstance(server, ServerAdapter):
            raise ValueError("Unknown or unsupported server: %r" % server)

        server.quiet = server.quiet or quiet
        if not server.quiet:
            _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server)))
            _stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
            _stderr("Hit Ctrl-C to quit.\n\n")

        if reloader:
            lockfile = os.environ.get('BOTTLE_LOCKFILE')
            bgcheck = FileCheckerThread(lockfile, interval)
            with bgcheck:
                server.run(app)
            if bgcheck.status == 'reload':
                sys.exit(3)
        else:
            server.run(app)
    except KeyboardInterrupt:
        pass
    except (SystemExit, MemoryError):
        raise
    except:
        if not reloader: raise
        if not getattr(server, 'quiet', quiet):
            print_exc()
        time.sleep(interval)
        sys.exit(3)

 

debug 默認是None, 下面這句並不執行。當debug is None
if debug is not None: _debug(debug) 

 



app = app or default_app()
if isinstance(app, basestring):
            app = load_app(app)
        if not callable(app):
            raise ValueError("Application is not callable: %r" % app)

 


app 由以前分析,是個AppStack 實例對象,app 有__call__方法,因此是可調用對象

測試:

>>> class Foo: pass >>> isinstance(Foo,type) True >>> callable(Foo) True >>> f1 = Foo() >>> callable(f1) False >>> class Foo: def __call__(self): pass >>> f1 = Foo() >>> callable(f1) True

 

只要一個對象有__call__ 屬性就是可調用對象

        if server in server_names:
            server = server_names.get(server)
        if isinstance(server, basestring):
            server = load(server)
        if isinstance(server, type):
            server = server(host=host, port=port, **kargs)
        if not isinstance(server, ServerAdapter):
            raise ValueError("Unknown or unsupported server: %r" % server)

 

server  = 'wsgiref' 
server_names = {
    'cgi': CGIServer,
    'flup': FlupFCGIServer,
    'wsgiref': WSGIRefServer,
    'waitress': WaitressServer,
    'cherrypy': CherryPyServer,
    'paste': PasteServer,
    'fapws3': FapwsServer,
    'tornado': TornadoServer,
    'gae': AppEngineServer,
    'twisted': TwistedServer,
    'diesel': DieselServer,
    'meinheld': MeinheldServer,
    'gunicorn': GunicornServer,
    'eventlet': EventletServer,
    'gevent': GeventServer,
    'geventSocketIO':GeventSocketIOServer,
    'rocket': RocketServer,
    'bjoern' : BjoernServer,
    'auto': AutoServer,
}

 

server_names 是個字典,key 是server 字符串,value 是對應的類。

至此,server = WSGIRefServer
執行下面代碼:


if isinstance(server, type): server = server(host=host, port=port, **kargs)
server 是一個WSGIRefServer 實例化的對象。
ServerAdapter 類是一個抽象類,全部服務器的類都繼承這個類。

到了關鍵的服務器啓動的一步
server.run(app)
 
server.run(app)

 

server.run(app)
來看WSGIRefServe 類
class WSGIRefServer(ServerAdapter):
    def run(self, app): # pragma: no cover
        from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
        from wsgiref.simple_server import make_server
        import socket

        class FixedHandler(WSGIRequestHandler):
            def address_string(self): # Prevent reverse DNS lookups please.
                return self.client_address[0]
            def log_request(*args, **kw):
                if not self.quiet:
                    return WSGIRequestHandler.log_request(*args, **kw)

        handler_cls = self.options.get('handler_class', FixedHandler)
        server_cls  = self.options.get('server_class', WSGIServer)

        if ':' in self.host: # Fix wsgiref for IPv6 addresses.
            if getattr(server_cls, 'address_family') == socket.AF_INET:
                class server_cls(server_cls):
                    address_family = socket.AF_INET6

        srv = make_server(self.host, self.port, app, server_cls, handler_cls)
        srv.serve_forever()
ServerAdapter類,初始化,run方法又子類重寫。
class ServerAdapter(object):
    quiet = False
    def __init__(self, host='127.0.0.1', port=8080, **options):
        self.options = options
        self.host = host
        self.port = int(port)

    def run(self, handler): # pragma: no cover
        pass
 

 

看到這裏,我想我要去研究WSGIREF 模塊的源碼去了。。。。。。。
server.run(app)主要看紅色部分的代碼,調用了wsgiref 模塊
調用wsgiref的make_server 實例了一個srv對象,srv調用serve_forever() 方法

simple_server.py
def
make_server( host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler ): """Create a new WSGI server listening on `host` and `port` for `app`""" server = server_class((host, port), handler_class) server.set_app(app) return server
 

 

實例了WSGIServer對象
WSGIServer 繼承自
HTTPServer ,
 
class WSGIServer(HTTPServer):

    """BaseHTTPServer that implements the Python WSGI protocol"""

    application = None

    def server_bind(self):
 

 

 
HTTPServer ,繼承自socketserver.TCPServer
class HTTPServer(socketserver.TCPServer):

    allow_reuse_address = 1    # Seems to make sense in testing environment

    def server_bind(self):
        """Override server_bind to store the server name."""
        socketserver.TCPServer.server_bind(self)
        host, port = self.server_address[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port

 

TCPServer繼承自BaseServer
建立一個socket對象
而後執行server_bind()方法和server_active()方法
server_bind()綁定ip端口,設置基本環境變量

 

 

 
class TCPServer(BaseServer):

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = False

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind() self.server_activate() except:
                self.server_close()
                raise
  
cket.getsockname()
 

 

 
TCPServer
 綁定
 TCPServer   類下的

def server_bind(self): """Called by constructor to bind the socket. May be overridden. """ if self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) self.server_address = self.socket.getsockname()
 

 

 監聽
    def server_activate(self):
        """Called by constructor to activate the server.

        May be overridden.

        """
        self.socket.listen(self.request_queue_size)
 

 

至此實例化結束,執行srv.serve_forever()  這個函數是處理請求,直到關機。使用輪詢,用到了selector 模塊,
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:
            # 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.
            with _ServerSelector() as selector:
                selector.register(self, selectors.EVENT_READ)

                while not self.__shutdown_request:
                    ready = selector.select(poll_interval)
                    if ready:
                        self._handle_request_noblock()

                    self.service_actions()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
 

 

note :這裏的self.__is_shut_down 是一個event 對象,
self.__is_shut_down = threading.Event()
 

 

 
self.__shutdown_request = False
 

 

 






















srv.serve_foreverT放到 適當放鬆的()
 

























































































































































































































 


















 

於洋 回到頂部

相關文章
相關標籤/搜索