1. 程式人生 > >Python網絡編程篇之socketserver

Python網絡編程篇之socketserver

做的 自定義 file read 開始 變量 spl 時間 多個

1.socketserver模塊和類

socketserver是標準庫中的一個高級模塊,目標是簡化很多樣板代碼(創建網絡客戶端和服務器所必須的代碼)

這個模塊封裝了socket編程所需要的各種各樣的類,現在可以使用類來編寫應用程序。

因為以面向對象的方式處理事務有助於組織數據,以及邏輯性地將功能放在正確的地方,應用程序現在是時間驅動的,這意味著只有在系統中的時間發生時,它才會工作。

SocketServer內部使用 IO多路復用 以及 “多線程” 和 “多進程” ,從而實現並發處理多個客戶端請求的Socket服務端。即:每個客戶端請求連接到服務器時,Socket服務端都會在服務器是創建一個“線程”或者“進程” 專門負責處理當前客戶端的所有請求。

socketserver模塊可以簡化網絡服務器的編寫,Python把網絡服務抽象成兩個主要的類

一個是Server類,用於處理連接相關的網絡操作

另外一個則是RequestHandler類,用於處理數據相關的操作。並且提供兩個MixIn 類,用於擴展 Server,實現多進程或多線程。

類的描述
1.BaseServer       包含核心服務器功能和 mix-in類的鉤子;僅用於推導,這樣不會創建這個類的實例;可以用TCPServer 或 UDPServer 創建類的實例
2.TCPServer/UDPServer             基礎的網絡同步TCP/UDP服務器
3.UnixStreamServer/UnixDatagramServer     基於文件的基礎同步TCp/UDP服務器
4.ForkingMixIn/ThreadingMixIn         核心派出或線程功能;只用做mix-in類與一個服務器類配合實現一些異步性;不能直接實例化這個類
5.ThreadingTCPServer/ThreadingUDPServer   ThreadingMixIn 和 TCPServer/UDPServer的組合
6.ForkingTCPServer/ForkingUDPServer      ForkingMixIn 和 TCPServer/UDPServer的組合
7.BaseRequestHandler 包含處理服務器請求的核心功能;僅僅用於推導,這樣無法創建這個類的實例;可以用StreamRequestHandler 或 DatagramRequestHandler 創建類的實例
8.StreamRequestHandler/DatagramRequestHandler 實現Tcp/UDP服務器的服務處理器

四個基礎類的繼承關系

+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+

這四個類使用"同步"來處理請求。只有處理完所有請求後,才可以開始處理新的請求!不適合使用在處理單個請求需要花費大量時間的場合。因為需要花費大量的計算時間,或者這因為它會返回大量的數據導致客戶端處理速度變得很慢。解決方法是創建單獨的進程或者線程處理每一個請求。在類內部的ForkingMixIn和ThreadingMixIn 組合可以支持"異步"的操作。

所以,讓你的socketserver並發起來, 必須選擇使用以下一個多並發的類

class socketserver.ForkingTCPServer

class socketserver.ForkingUDPServer

class socketserver.ThreadingTCPServer

class socketserver.ThreadingUDPServer

2.ThreadingTCPServer

ThreadingTCPServer實現的Soket服務器內部會為每個client創建一個 “線程”,該線程用來和客戶端進行交互。

首先,必須通過繼承BaseRequestHandler類並重寫handle()方法來創建請求處理程序類; 這個方法將處理傳入的請求。
其次,必須實例化其中一個服務器類,並將其傳遞給服務器的地址和請求處理程序類。
然後,調用服務器對象的handle_request()or serve_forever()方法來處理一個或多個請求。
最後,調用server_close()關閉套接字。

ThreadingTCPServer---->TCPServer  ------>BaseServer------>RequestHandlerClass
    |                    (__init__)                            server_forever 
    |                                                        finish_request                                             
    |-----> ThreadingMixIn.process_request()
    |----->ThreadingMixIn.process_request_thread()

內部調用流程為:

  • 啟動服務端程序
  • 執行 TCPServer.__init__ 方法,創建服務端Socket對象並綁定 IP 和 端口
  • 執行 BaseServer.__init__ 方法,將自定義的繼承自SocketServer.BaseRequestHandler 的類 MyRequestHandle賦值給self.RequestHandlerClass
  • 執行 BaseServer.server_forever 方法,While 循環一直監聽是否有客戶端請求到達 ...
  • 當客戶端連接到達服務器
  • 執行 ThreadingMixIn.process_request 方法,創建一個 “線程” 用來處理請求
  • 執行 ThreadingMixIn.process_request_thread 方法
  • 執行 BaseServer.finish_request 方法,執行 self.RequestHandlerClass() 即:執行 自定義 MyRequestHandler 的構造方法(自動調用基類BaseRequestHandler的構造方法,在該構造方法中又會調用 MyRequestHandler的handle方法)

服務端

# -*- coding: utf-8 -*-
# 2017/11/25 20:15
import socketserver

class MyServer(socketserver .BaseRequestHandler):
    def handle(self):
        # print self.request,self.client_address,self.server
        conn = self.request
        conn.sendall(bytes(歡迎致電 10086,0轉人工服務.,encoding=utf8))
        Flag = True
        while Flag:
            data = conn.recv(1024)
            data = str(data, encoding=utf8)
            if data == exit:
                Flag = False
            elif data == 0:
                conn.sendall(bytes(通過可能會被錄音,encoding=utf8))
            else:
                conn.sendall(bytes(請重新輸入.,encoding=utf8))

if __name__ == __main__:
    server = socketserver .ThreadingTCPServer((127.0.0.1,8009),MyServer)
    server.serve_forever()

服務端源碼模擬

技術分享圖片
 1 # -*- coding: utf-8 -*-
 2 # 2017/11/25 20:37
 3 import socket
 4 import threading
 5 import select
 6 
 7 
 8 def process(request, client_address):
 9     print(request,client_address)
10     conn = request
11     conn.sendall(bytes(歡迎致電 10086,請輸入1xxx,0轉人工服務., encoding=utf8))
12     Flag = True
13     while Flag:
14         data = conn.recv(1024)
15         data = str(data, encoding=utf8)
16         if data == exit:
17             Flag = False
18         elif data == 0:
19             print(data)
20             conn.sendall(bytes(通過可能會被錄音.balabala一大推, encoding=utf8))
21         else:
22             conn.sendall(bytes(請重新輸入., encoding=utf8))
23 
24 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25 sk.bind((127.0.0.1,8002))
26 sk.listen(5)
27 
28 while True:
29     r, w, e = select.select([sk,],[],[],1)
30     print(looping)
31     if sk in r:
32         print(get request)
33         request, client_address = sk.accept()
34         t = threading.Thread(target=process, args=(request, client_address))
35         t.daemon = False
36         t.start()
37 sk.close()
View Code

如精簡代碼可以看出,SocketServer的ThreadingTCPServer之所以可以同時處理請求得益於 selectThreading 兩個東西,其實本質上就是在服務器端為每一個客戶端創建一個線程,當前線程用來處理對應客戶端的請求,所以,可以支持同時n個客戶端鏈接(長連接)。

客戶端

# -*- coding: utf-8 -*-
# 2017/11/25 20:16
import socket
ip_port = (127.0.0.1,8009)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)

while True:
    data = sk.recv(1024)
    data = str(data, encoding=utf8)
    print(receive:,data)
    inp = input(please input:)
    sk.sendall(bytes(inp,encoding="utf8"))
    if inp == exit:
        break

sk.close()

ForkingTCPServer只是將 ThreadingTCPServer 實例中的代碼:

SocketServer的ThreadingTCPServer之所以可以同時處理請求得益於 selectos.fork 兩個東西,其實本質上就是在服務器端為每一個客戶端創建一個進程,當前新創建的進程用來處理對應客戶端的請求,所以,可以支持同時n個客戶端鏈接(長連接)。

server = SocketServer.ThreadingTCPServer((‘127.0.0.1‘,8009),MyRequestHandler)

變更為: server = SocketServer.ForkingTCPServer((‘127.0.0.1‘,8009),MyRequestHandler)

3.class socketserver.BaseServer(server_address, RequestHandlerClass) 主要有以下方法

類socketserver.BaseServer(server_address,RequestHandlerClass)
這是模塊中所有服務器對象的超類。它定義了下面給出的接口,但不實現大多數在子類中完成的方法。這兩個參數存儲在相應的server_address和RequestHandlerClass屬性中。

fileno()
返回服務器正在偵聽的套接字的整數文件描述符。這個函數通常被傳遞給選擇器,允許在同一個進程中監視多個服務器。

handle_request()
處理一個請求。該函數按順序調用以下方法:get_request(),verify_request()和process_request()。如果處理程序類的用戶提供的handle()方法引發異常,則將調用服務器的handle_error()方法。如果在超時秒內沒有收到請求,則會調用handle_timeout(),並返回handle_request()。

serve_forever(POLL_INTERVAL = 0.5)
處理請求直到顯式關閉()請求。輪詢關閉每個poll_interval秒。忽略超時屬性。它還調用service_actions(),子類或mixin可以使用它來提供特定於給定服務的操作。例如,ForkingMixIn類使用service_actions()來清理僵屍子進程。

在版本3.3中進行了更改:將service_actions調用添加到了serve_forever方法。

service_actions()
這在serve_forever()循環中被調用。這個方法可以被子類或mixin類覆蓋,以執行特定於給定服務的操作,例如清理操作。

3.3版本中的新功能

shutdown()
告訴serve_forever()循環停止並等待,直到它結束。

server_close()
清理服務器。可能會被覆蓋。

address_family
服務器套接字所屬的協議族。通常的例子是socket.AF_INET和socket.AF_UNIX。

RequestHandlerClass
用戶提供的請求處理程序類;這個類的一個實例是為每個請求創建的。

server_address

服務器正在偵聽的地址。地址格式因協議族而異,有關詳細信息,請參閱套接字模塊的文檔。對於Internet協議,這是一個包含給出地址的字符串的元組,以及一個整數端口號:(‘127.0.0.1‘,80)。

socket
服務器將偵聽傳入請求的套接字對象。

服務器類支持以下類變量:

allow_reuse_address
服務器是否允許重用地址。這默認為False,可以在子類中設置來更改策略。

request_queue_size
請求隊列的大小。如果處理單個請求需要很長時間,則在服務器繁忙時到達的所有請求都會被放入一個隊列中,最多為request_queue_size請求。一旦隊列已滿,來自客戶端的進一步請求將會得到“連接被拒絕”錯誤。默認值通常是5,但這可以由子類覆蓋。

socket_type
服務器使用的套接字的類型; socket.SOCK_STREAM和socket.SOCK_DGRAM是兩個常見的值。

timeout
超時持續時間(以秒為單位);如果不需要超時,則超時。如果handle_request()在超時期限內沒有收到傳入的請求,則調用handle_timeout()方法。

有許多服務器方法可以被TCPServer等基本服務器類的子類覆蓋;這些方法對服務器對象的外部用戶沒有用處。

finish_request()
實際上通過實例化RequestHandlerClass並調用其handle()方法來處理請求。

get_request()
必須接受來自套接字的請求,並返回包含要用於與客戶端通信的新套接字對象的2元組以及客戶端的地址。

handle_error(request,client_address)
如果RequestHandlerClass實例的handle()方法引發異常,則調用此函數。默認操作是將回溯打印到標準輸出,並繼續處理更多的請求。

handle_timeout()
當timeout屬性被設置為None以外的值時,該函數被調用,超時時間已經過去,沒有收到請求。派生服務器的默認動作是收集退出的任何子進程的狀態,而在線程服務器中,這個方法什麽也不做。

process_request(request,client_address)
調用finish_request()來創建RequestHandlerClass的一個實例。如果需要,這個函數可以創建一個新的進程或線程來處理請求; ForkingMixIn和ThreadingMixIn類都是這樣做的。

server_activate()
由服務器的構造函數調用以激活服務器。 TCP服務器的默認行為只是在服務器套接字上調用listen()。可能會被覆蓋。

server_bind()
由服務器的構造函數調用,將套接字綁定到所需的地址。可能會被覆蓋。

verify_request(request,client_address)
必須返回一個布爾值;如果值為True,

Python網絡編程篇之socketserver