通用socket server 類別
此模組盡力從各種不同的方面定義server:
對於socket-based servers:
-- address family:
- AF_INET{,6}: IP socket (default)
- AF_UNIX: Unix domain sockets
- others, 如 AF_DECNET (見) (不常用)
-- socket type:
- SOCK_STREAM (可靠連線 TCP)
- SOCK_DGRAM (UDP)
對於request-based servers:
-- client address在發出進一步的請求之前需要認證(這實際上把所有需要發出請求的進程在通過認證之前給阻塞住了)
-- 如何處理多重請求:
- 同步 (一次只能處理一個請求)
- forking (fork一個新的程序來處理一個請求)
- threading (建立一個新的執行緒來處理一個請求)
在這個模組的各種類別中,最簡單的伺服器類型就是synchronous TCP/IP server。這是一個糟糕的類別設計,但是也保存了一些設計的類型理念。
-- address family:
- AF_INET{,6}: IP socket (default)
- AF_UNIX: Unix domain sockets
- others, 如 AF_DECNET (見
-- socket type:
- SOCK_STREAM (可靠連線 TCP)
- SOCK_DGRAM (UDP)
對於request-based servers:
-- client address在發出進一步的請求之前需要認證(這實際上把所有需要發出請求的進程在通過認證之前給阻塞住了)
-- 如何處理多重請求:
- 同步 (一次只能處理一個請求)
- forking (fork一個新的程序來處理一個請求)
- threading (建立一個新的執行緒來處理一個請求)
在這個模組的各種類別中,最簡單的伺服器類型就是synchronous TCP/IP server。這是一個糟糕的類別設計,但是也保存了一些設計的類型理念。
下面是五個類別的繼承關係圖表,其中的四個代表四種類型的同步伺服器:
+--------------+
| BaseServer |
+--------------+
|
v
+------------+ +----------------------+
| TCPServer |------->| UnixStreamServer |
+------------+ +----------------------+
|
v
+-------------+ +--------------------------+
| UDPServer |------->| UnixDatagramServer |
+-------------+ +--------------------------+
注意:UnixDatagramServer繼承於UDPServer,而不是UnixStreamServer,IP和Unix stream server之間僅有的差異就是address family,兩個伺服器類別的內容多數是簡單的重複。
+--------------+
| BaseServer |
+--------------+
|
v
+------------+ +----------------------+
| TCPServer |------->| UnixStreamServer |
+------------+ +----------------------+
|
v
+-------------+ +--------------------------+
| UDPServer |------->| UnixDatagramServer |
+-------------+ +--------------------------+
注意:UnixDatagramServer繼承於UDPServer,而不是UnixStreamServer,IP和Unix stream server之間僅有的差異就是address family,兩個伺服器類別的內容多數是簡單的重複。
forking和threading 可以建立用於ForkingMixIn和TreadingMixIn mix-in類別。例如: threading UDP server類別會被如下方式建立:
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
(詳細可見後文範例)
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
(詳細可見後文範例)
Mix-in 這個類別必須先實現,因為它重寫了定義UDPServer的方法。設定不同的成員變數也改變了基本的伺服器建構方法。
為了實現一個服務,你必須從基底類別BaseRequestHandler重新定義它的handle方法。然後透過把服務類別與你重寫的Handle方法類別結合,以此運行新的服務類別。
請求處理類別的TCP和UDP的方式是不同的,這個可以透過使用請求處理的子類別StreamRequestHandler或DatagramRequestHandler來隱藏。
當然,你還可以思考其他的方法。
例如,如果服務中包含請求修改的記憶體的狀態,那麼使用forking server就沒有任何意義(因為在子程序中修改將不對父程序的初始化狀態有影響,父程序也不會把這個修改的參數傳遞給其他子進程)。在這種情況下,你可以使用threading server,而且你更有可能需要用到“鎖”,以此來避免兩個請求同時到達而使伺服器狀態產生衝突。
此外,如果你在搭建如HTTP伺服器等,所有的資料都會儲存在外部(如檔案系統中),當客戶端的一個請求被處理時,並且客戶端的讀取資料的速度很慢,synchronous class將會使服務不做出回應,這可能需要維持很長時間。
在某些情況下,請求同步可能需要適當的方法,但是為了在子程序中完成請求要受到請求資料的影響。這可以透過使用同步伺服器來實現,並且在請求處理類別中的Handle方法中明確指定fork的進程。
另一種處理多個同時發生的請求的方法是維繫一張明確的完成請求的表單,使用select()方法來判定哪個請求應該在接下來做出回應(或判斷是否要處理新到來的請求) ,當每個客戶端需要建立很長時間的連線時,這對stream services來說非常重要。 (前提是不使用執行緒和子程序的方法)
例如,如果服務中包含請求修改的記憶體的狀態,那麼使用forking server就沒有任何意義(因為在子程序中修改將不對父程序的初始化狀態有影響,父程序也不會把這個修改的參數傳遞給其他子進程)。在這種情況下,你可以使用threading server,而且你更有可能需要用到“鎖”,以此來避免兩個請求同時到達而使伺服器狀態產生衝突。
此外,如果你在搭建如HTTP伺服器等,所有的資料都會儲存在外部(如檔案系統中),當客戶端的一個請求被處理時,並且客戶端的讀取資料的速度很慢,synchronous class將會使服務不做出回應,這可能需要維持很長時間。
在某些情況下,請求同步可能需要適當的方法,但是為了在子程序中完成請求要受到請求資料的影響。這可以透過使用同步伺服器來實現,並且在請求處理類別中的Handle方法中明確指定fork的進程。
另一種處理多個同時發生的請求的方法是維繫一張明確的完成請求的表單,使用select()方法來判定哪個請求應該在接下來做出回應(或判斷是否要處理新到來的請求) ,當每個客戶端需要建立很長時間的連線時,這對stream services來說非常重要。 (前提是不使用執行緒和子程序的方法)
二、用到的所有的類別方法
<span style="color: #0000ff;">import</span><span style="color: #000000;"> socket </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> select </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> sys </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> os </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> errno </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> threading </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError: </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> dummy_threading as threading </span><span style="color: #800080;">__all__</span> = [<span style="color: #800000;">"</span><span style="color: #800000;">TCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingTCPServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ThreadingTCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">BaseRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">StreamRequestHandler</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">DatagramRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingMixIn</span><span style="color: #800000;">"</span>, <span style="color: #800000;">"</span><span style="color: #800000;">ForkingMixIn</span><span style="color: #800000;">"</span><span style="color: #000000;">] </span><span style="color: #0000ff;">if</span> hasattr(socket, <span style="color: #800000;">"</span><span style="color: #800000;">AF_UNIX</span><span style="color: #800000;">"</span><span style="color: #000000;">): </span><span style="color: #800080;">__all__</span>.extend([<span style="color: #800000;">"</span><span style="color: #800000;">UnixStreamServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UnixDatagramServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixStreamServer</span><span style="color: #800000;">"</span><span style="color: #000000;">, </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixDatagramServer</span><span style="color: #800000;">"</span>])
三、BaseServer和BaseRequestHandler
Python把網路服務抽象化成兩個主要的類,一個是Server類,用於處理連接相關的網路操作,另外一個則是RequestHandler類,用於處理與資料相關的操作。並且提供兩個MixIn 類,用於擴展 Server,實現多進程或多執行緒。在建置網路服務的時候,Server 和 RequestHandler 並不是分開的,RequestHandler的實例物件在Server 內配合 Server工作。
3.1 BaseServer分析
BaseServer可供外部呼叫的方法:
- __init__(server_address, RequestHandlerClass)
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request() # if you do not use serve_forever()
- fileno() -> int # for select()
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request() # if you do not use serve_forever()
- fileno() -> int # for select()
即我們可以透過init初始化,對外提供serve_forever()和handle_request()方法
3.1.1 init初始化
timeout =<span style="color: #000000;"> None </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor. May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.server_address </span>=<span style="color: #000000;"> server_address self.RequestHandlerClass </span>=<span style="color: #000000;"> RequestHandlerClass self.</span><span style="color: #800080;">__is_shut_down</span> =<span style="color: #000000;"> threading.Event() self.</span><span style="color: #800080;">__shutdown_request</span> = False
__init__的主要作用就是建立server對象,並初始化server_address和RequestHandlerClass。
server_address就是包含主機位址和連接埠的元組。
3.1.2 serve_forever
<span style="color: #0000ff;">def</span> serve_forever(self, poll_interval=0.5<span style="color: #000000;">): </span><span style="color: #800000;">"""</span><span style="color: #800000;">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. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.</span><span style="color: #800080;">__is_shut_down</span><span style="color: #000000;">.clear() </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">while</span> <span style="color: #0000ff;">not</span> self.<span style="color: #800080;">__shutdown_request</span><span style="color: #000000;">: r, w, e </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [], poll_interval) </span><span style="color: #0000ff;">if</span> self <span style="color: #0000ff;">in</span><span style="color: #000000;"> r: self._handle_request_noblock() </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">: self.</span><span style="color: #800080;">__shutdown_request</span> =<span style="color: #000000;"> False self.</span><span style="color: #800080;">__is_shut_down</span>.set()
這裡用到了select()函數,即server_forever接受了一個poll_interval=0.5的參數傳入,這表示用於select輪詢的時間,然後進入一個無限循環中,在這個循環中,select每隔poll_interval秒輪詢一次(阻塞於此),以此來進行網路IO的監聽。一旦有新的網路連線請求到來,則會呼叫_handle_request_noblock()方法處理新的連線。
3.1.3 _handle_request_noblock()
<span style="color: #0000ff;">def</span><span style="color: #000000;"> _handle_request_noblock(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request, without blocking. I assume that select.select has returned that the socket is readable before this function was called, so there should be no risk of blocking in get_request(). </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">try</span><span style="color: #000000;">: request, client_address </span>=<span style="color: #000000;"> self.get_request() </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.verify_request(request, client_address): </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.process_request(request, client_address) </span><span style="color: #0000ff;">except</span><span style="color: #000000;">: self.handle_error(request, client_address) self.shutdown_request(request)</span>
英文说明已经说的很明确,该方法处理的是一个非阻塞请求,首先通过get_request()方法获取连接,具体实现在其子类,一旦获取了连接,立即调用verify_request认证连接信息,通过认证,则调用process_request()方法处理请求,如果中途出现错误,则调用handle_error处理错误,同时,调用shutdown_request()方法结束这个连接。
那下面我们就先看上面提到的其他几个函数调用:
<span style="color: #0000ff;">def</span><span style="color: #000000;"> verify_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Verify the request. May be overridden. Return True if we should proceed with this request. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> True </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> process_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Call finish_request. Overridden by ForkingMixIn and ThreadingMixIn. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.finish_request(request, client_address) self.shutdown_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_error(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle an error gracefully. May be overridden. The default is to print a traceback and continue. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40 <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">Exception happened during processing of request from</span><span style="color: #800000;">'</span><span style="color: #000000;">, </span><span style="color: #0000ff;">print</span><span style="color: #000000;"> client_address </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> traceback traceback.print_exc() </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX But this goes to stderr!</span> <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40 <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.close_request(request)</span><pre name="<span" style="color: #800000;">"<span style="color: #800000;">code</span><span style="color: #800000;">"</span> <span style="color: #0000ff;">class</span>=<span style="color: #800000;">"</span><span style="color: #800000;">python</span><span style="color: #800000;">"</span>> <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish_request(self, request, client_address): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Finish one request by instantiating RequestHandlerClass.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.RequestHandlerClass(request, client_address, self)</span>
verify_request()方法对request进行验证,通常会被子类重写。
process_request需要注意一下,它被ForkingMixIn 和 ThreadingMixIn重写,因此是mixin的入口,ForkingMixIn和ThreadingMixIn分别进行多进程和多线程的配置,并且调用finish_request()完成请求,调用shutdown_request()结束请求。
handle_error也可以被子类重写,打印错误的信息和客户端地址。
finish_request()处理完毕请求,在__init__中创建requestHandler对象,并通过requestHandler做出具体的处理。
3.1.4 handle_request()
3.1.4 handle_request()
<span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_request(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request, possibly blocking. Respects self.timeout. </span><span style="color: #800000;">"""</span> <span style="color: #008000;">#</span><span style="color: #008000;"> Support people who used socket.settimeout() to escape</span> <span style="color: #008000;">#</span><span style="color: #008000;"> handle_request before self.timeout was available.</span> timeout =<span style="color: #000000;"> self.socket.gettimeout() </span><span style="color: #0000ff;">if</span> timeout <span style="color: #0000ff;">is</span><span style="color: #000000;"> None: timeout </span>=<span style="color: #000000;"> self.timeout </span><span style="color: #0000ff;">elif</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None: timeout </span>=<span style="color: #000000;"> min(timeout, self.timeout) fd_sets </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [], timeout) </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> fd_sets[0]: self.handle_timeout() </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> self._handle_request_noblock()</span>
上面已经提到,如果你没有用到server_forever()方法,说明你希望使用的是阻塞请求来处理连接,如英文描述所说,该方法只是处理一个阻塞的请求,仍然使用select()方法轮询来监听网络连接,但是需要考虑时间超时影响,一旦超时,调用handle_timeout()方法处理超时,一般在子类重写该方法;如果在超时之前监听到了网络的连接请求,则同server_forever一样,调用_handle_request_noblock()方法,完成对新的连接的请求处理。
3.2 BaseRequestHandler分析
<span style="color: #0000ff;">class</span><span style="color: #000000;"> BaseRequestHandler: </span><span style="color: #800000;">"""</span><span style="color: #800000;">Base class for request handler classes. This class is instantiated for each request to be handled. The constructor sets the instance variables request, client_address and server, and then calls the handle() method. To implement a specific service, all you need to do is to derive a class which defines a handle() method. The handle() method can find the request as self.request, the client address as self.client_address, and the server (in case it needs access to per-server information) as self.server. Since a separate instance is created for each request, the handle() method can define arbitrary other instance variariables. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, request, client_address, server): self.request </span>=<span style="color: #000000;"> request self.client_address </span>=<span style="color: #000000;"> client_address self.server </span>=<span style="color: #000000;"> server self.setup() </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.handle() </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">: self.finish() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): </span><span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> handle(self): </span><span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): </span><span style="color: #0000ff;">pass</span>
以上描述说明,所有requestHandler都继承BaseRequestHandler基类,该类会处理每一个请求。在__init__中初始化实例变量request、client_address、server,然后调用handle()方法完成请求处理。那么,我们唯一需要做的就是重写好Handle()方法,处理所有的请求。
总结:构建一个网络服务,需要一个BaseServer用于处理网络IO,同时在内部创建requestHandler对象,对所有具体的请求做处理。
四、各种子类
4.1 由BaseServer衍生的子类
4.1.1 TCPServer
在BaseServer基础上增加了一个TCP的socket连接,使用server_bind、server_activate、server_close处理TCP启停等操作,同时增加了get_request、shutdown_request、close_request处理客户端请求。
<span style="color: #0000ff;">class</span><span style="color: #000000;"> TCPServer(BaseServer): address_family </span>=<span style="color: #000000;"> socket.AF_INET socket_type </span>=<span style="color: #000000;"> socket.SOCK_STREAM request_queue_size </span>= 5<span style="color: #000000;"> allow_reuse_address </span>=<span style="color: #000000;"> False </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span>(self, server_address, RequestHandlerClass, bind_and_activate=<span style="color: #000000;">True): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor. May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> BaseServer.</span><span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass) self.socket </span>=<span style="color: #000000;"> socket.socket(self.address_family, self.socket_type) </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> bind_and_activate: </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.server_bind() self.server_activate() </span><span style="color: #0000ff;">except</span><span style="color: #000000;">: self.server_close() </span><span style="color: #0000ff;">raise</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> server_bind(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to bind the socket. May be overridden. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.allow_reuse_address: self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, </span>1<span style="color: #000000;">) self.socket.bind(self.server_address) self.server_address </span>=<span style="color: #000000;"> self.socket.getsockname() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to activate the server. May be overridden. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.socket.listen(self.request_queue_size) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_close(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean-up the server. May be overridden. </span><span style="color: #800000;">"""</span><span style="color: #000000;"> self.socket.close() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> fileno(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Return socket file number. Interface required by select(). </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.fileno() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Get the request and client address from the socket. May be overridden. </span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.accept() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #008000;">#</span><span style="color: #008000;">explicitly shutdown. socket.close() merely releases</span> <span style="color: #008000;">#</span><span style="color: #008000;">the socket and waits for GC to perform the actual close.</span> <span style="color: #000000;"> request.shutdown(socket.SHUT_WR) </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #0000ff;">pass</span> <span style="color: #008000;">#</span><span style="color: #008000;">some platforms may raise ENOTCONN here</span> <span style="color: #000000;"> self.close_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request): </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean up an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> request.close()</span>
在BaseServer基础上增加了一个TCP的socket连接,使用server_bind、server_activate、server_close处理TCP启停等操作,同时增加了get_request、shutdown_request、close_request处理客户端请求。
4.1.2 UDPServer
<span style="color: #0000ff;">class</span><span style="color: #000000;"> UDPServer(TCPServer): </span><span style="color: #800000;">"""</span><span style="color: #800000;">UDP server class.</span><span style="color: #800000;">"""</span><span style="color: #000000;"> allow_reuse_address </span>=<span style="color: #000000;"> False socket_type </span>=<span style="color: #000000;"> socket.SOCK_DGRAM max_packet_size </span>= 8192 <span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self): data, client_addr </span>=<span style="color: #000000;"> self.socket.recvfrom(self.max_packet_size) </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (data, self.socket), client_addr </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to call listen() for UDP.</span> <span style="color: #0000ff;">pass</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to shutdown anything.</span> <span style="color: #000000;"> self.close_request(request) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request): </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to close anything.</span> <span style="color: #0000ff;">pass</span>
继承自TCPServer,将socket改为了SOCK_DGRAM型,并修改了get_request,用于从SOCK_DGRAM中获取request。同时server_activate、shutdown_request、close_request都改成了空(UDP不需要),比TCP简单一些。
4.2 由BaseRequestHandler衍生的子类
4.2.1 StreamRequestHandler
<span style="color: #0000ff;">class</span><span style="color: #000000;"> StreamRequestHandler(BaseRequestHandler): rbufsize </span>= -1<span style="color: #000000;"> wbufsize </span>=<span style="color: #000000;"> 0 timeout </span>=<span style="color: #000000;"> None disable_nagle_algorithm </span>=<span style="color: #000000;"> False </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): self.connection </span>=<span style="color: #000000;"> self.request </span><span style="color: #0000ff;">if</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None: self.connection.settimeout(self.timeout) </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> self.disable_nagle_algorithm: self.connection.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, True) self.rfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">rb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.rbufsize) self.wfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">wb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.wbufsize) </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.wfile.closed: </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: self.wfile.flush() </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error: </span><span style="color: #008000;">#</span><span style="color: #008000;"> An final socket error may have occurred here, such as</span> <span style="color: #008000;">#</span><span style="color: #008000;"> the local error ECONNABORTED.</span> <span style="color: #0000ff;">pass</span><span style="color: #000000;"> self.wfile.close() self.rfile.close()</span>
最主要的功能是根据socket生成了读写socket用的两个文件对象(可以理解为句柄)rfile和wfile
4.2.2 DatagramRequestHandler
<span style="color: #0000ff;">class</span><span style="color: #000000;"> DatagramRequestHandler(BaseRequestHandler): </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX Regrettably, I cannot get this working on Linux;</span> <span style="color: #008000;">#</span><span style="color: #008000;"> s.recvfrom() doesn't return a meaningful client address.</span> <span style="color: #800000;">"""</span><span style="color: #800000;">Define self.rfile and self.wfile for datagram sockets.</span><span style="color: #800000;">"""</span> <span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self): </span><span style="color: #0000ff;">try</span><span style="color: #000000;">: </span><span style="color: #0000ff;">from</span> cStringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError: </span><span style="color: #0000ff;">from</span> StringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO self.packet, self.socket </span>=<span style="color: #000000;"> self.request self.rfile </span>=<span style="color: #000000;"> StringIO(self.packet) self.wfile </span>=<span style="color: #000000;"> StringIO() </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self): self.socket.sendto(self.wfile.getvalue(), self.client_address)</span>
同样是生成rfile和wfile,但UDP不直接关联socket。这里的rfile是直接由从UDP中读取的数据生成的,wfile则是新建了一个StringIO,用于写数据。
(题目起的有点大,部分剖析的不好,等之后再往祖坟上刨。。。。^-^)
参考博客:http://www.cnblogs.com/tuzkee/p/3573210.html
http://www.jianshu.com/p/357e436936bf
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章
刺客信條陰影:貝殼謎語解決方案
3 週前ByDDD
Windows 11 KB5054979中的新功能以及如何解決更新問題
3 週前ByDDD
在哪裡可以找到原子中的起重機控制鑰匙卡
3 週前ByDDD
<🎜>:死鐵路 - 如何完成所有挑戰
4 週前ByDDD
Atomfall指南:項目位置,任務指南和技巧
1 個月前ByDDD

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

Atom編輯器mac版下載
最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1
強大的PHP整合開發環境

WebStorm Mac版
好用的JavaScript開發工具