socketserver 模塊簡介

1、socketserver模塊簡介

socketserver模塊簡化了網絡編程,模塊下有五個服務類:BaseServer、TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer 。這五個類的關係以下:html

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

一、四個基本的同步服務器類簡介: 

class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True):TCP數據流服務器
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True):UDP數據報服務器python

class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True):僅限於Unix系統的,Unix套接字流
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass, bind_and_activate=True):僅限於Unix系統的,Unix數據報編程

他們的參數意義相同,以下:服務器

server_address:服務器的地址,他應該是一個元組包含地址和端口如:('192.168.1.1',9000),網絡

RequestHandlerClass:咱們自定義的類,類中必須重寫handle()方法。用於處理全部socket請求。多線程

bind_and_activate:若是爲True,將自動調用server_bind()和server_activate()。框架

 這四個類運行時只能處理一個請求,也就是一個服務端只能對應一個客戶端,這對於咱們未來要編寫的FTP服務器可能不適用,由於咱們但願一個服務能處理多個客戶端,下面咱們來看socketserver爲咱們提供的兩個處理異步的類。異步

二、異步服務器類簡介

class socketserver.ForkingMixIn:啓用多進程
class socketserver.ThreadingMixIn:啓用多線程socket

建立異步服務的方法很是簡單,下面建立一個簡單的TCP框架爲例:函數

import socketserver

class MyTCPServer(socketserver.BaseRequestHandler):  # 自定義TCP服務類,必須繼承socketserver.BaseRequestHandler
    def handle(self):  # 重寫此類,
        '''
        咱們要處理的任務
        :return: 
        '''
        self.fun()  # 執行咱們的任務
    def fun(self):
        print('Hello World')

class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): # 類名隨便起,必需要繼承這兩個類
    pass
if __name__ == "__main__":
    ip_port = ("127.0.0.1", 9000)  # TCP服務器的地址和端口

    with socketserver.ThreadingTCPServer(ip_port, MyTCPServer) as server:
        server.serve_forever()  # 開啓TCP服務

這樣咱們就簡單的建立了一個異步TCP服務器框架,其實ThreadingTCPServer這個類咱們不用本身建立,由於socketserver已經爲咱們建立好了,以下:

class ForkingUDPServer(ForkingMixIn, UDPServer): pass  # 多進程UDP服務器
class ForkingTCPServer(ForkingMixIn, TCPServer): pass  # 多進程TCP服務器
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass  # 多線程UDP服務器
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass  # 多線程TCP服務器

若是以爲socketserver提供的這四個類不是你想要的,那麼你就能夠像上面那樣本身定義,上面都是服務類,經過服務類實例化對象,可是目前還不知道對象擁有哪些方法,由於這些服務類都是繼承的BaseServer類,因此方法都在BaseServer類中,有些方法只是定義了接口,在子類中實現的。

三、服務對象經常使用方法

class socketserver.BaseServer(server_address, RequestHandlerClass):

fileno():返回服務器正在監聽的套接字的文件描述符(int類型的數字)。此函數最常傳遞給選擇器,以容許監視同一進程中的多個服務器。

handle_request():處理單個請求。此函數按順序調用如下方法:get_request()、verify_request()和process_request()。若是handler類的用戶提供的handle()方法引起異常,則將調用服務器的handle_error()方法。若是在超時秒內未收到請求,將調用handle_timeout(),並返回handle_request()。

serve_forever(poll_interval=0.5):定時任務,一般是在一個線程中,每poll_interval秒輪詢一次,直到調用shutdown中止。

service_actions():該函數被serve_forever定時函數重複調用,這個方法咱們能夠繼承BaseServer,而後重寫此方法。

shutdown():此方法用於中止serve_forever()定時任務。

socket:socket對象。

socket_type:socket套接字類型,TCP,UDP等。

allow_reuse_address:服務器是否容許地址的重用。默認爲false ,而且可在子類中更改。

address_family:設置socket套接字家族。

server_address:值是一個元組,socket服務器地址和監聽的端口。

server_activate():服務器將處於監聽狀態,該函數可被重寫,其實他的內部就是self.socket.listen(self.request_queue_size)。

server_bind():將socket綁定到地址上,能夠被重寫。

get_request():此方法的前提是必須接收到來自套接字的請求,返回一個元組(與客戶端通訊的新套接字對象,客戶端地址)。其實該方法就是將self.socket.accept()的結果返回。

server_close():關閉服務(關閉socket),此方法可被重寫。

RequestHandlerClass:值是類名,這個類是咱們定義的用於建立實例處理咱們的請求,如上面TCP異步框架中的MyTCPServer,這個類就是RequestHandlerClass的值。

request_queue_size:請求隊列的大小。若是處理單個請求須要很長時間,在服務器繁忙時會將請求放到隊列中,當請求數達到request_queue_size的值時。來自客戶端的進一步請求將獲得一個「拒絕鏈接」錯誤。默認值一般是5,可是這個值能夠被子類覆蓋。

finish_request(request, client_address):此方法會實例化RequestHandlerClass並調用它的handle()方法來實際處理請求。

process_request(request,client_address):調用finish_request()來建立RequestHandlerClass的一個實例。咱們能夠本身建立線程池或進程池來調用這個函數,實現服務器處理多個請求的問題,ForkingMixIn和ThreadingMixIn類就是這樣作的。

handle_error(request,client_address):若是RequestHandlerClass實例的handle()方法引起異常,則調用此函數。默認操做是將回溯打印到標準錯誤,並繼續處理其餘請求。在版本3.6中更改:如今僅調用從Exception類派生的異常。

timeout:超時時間(以秒爲單位),若是是None,會一直阻塞。若是設置了timeout,handle_request()在超時期間沒有收到傳入請求,則調用handle_timeout()方法。

handle_timeout():當timeout屬性被設置爲None之外的值,而且超時週期已通過去而沒有收到任何請求時,將調用此函數。多進程 服務器的默認操做是收集已退出的任何子進程的狀態,而在線程服務器中,此方法不執行任何操做。

verify_request(request,client_address):返回一個布爾值;若是值爲真,請求將被處理,若是值爲假,請求將被拒絕。能夠重寫此函數以實現服務器的訪問控制。默認實現只是一句return True。

上面這些都是服務對象的方法,下面來介紹處理socket請求類BaseRequestHandler。

四、客戶端請求處理類BaseRequestHandler

class socketserver.BaseRequestHandler:

這是全部socket請求處理程序的基類。它只定義了接口,而沒有實現,若是想要使用接口,咱們首先繼承BaseRequestHandler,而後在子類中重寫這些方法。每一個socket請求處理程序子類必須重寫handle()方法,由於該方法是用於處理全部socket請求。該類的方法以下:

setup():在handle()方法以前調用,執行初始化操做。 默認不執行任何操做,咱們能夠重寫此方法來實現程序的初始化。

handle():全部socket請求任務都是在這個函數內部完成的,咱們在子類中必須重寫此方法,並處理socket請求,由於默認基類中的handle()的實現不執行任何操做。

finish():在handle()方法以後調用以執行清理操做。默認實現不執行任何操做。若是setup()引起異常,則不會調用此函數。

雖然上面的接口都只是定義而沒有實現,可是它的實例屬性仍是頗有用的;

self.request;客戶端和服務端的鏈接對象,用於發送數據,接收數據。 

self.client_address:socket客戶端地址 。

self.server:socket服務端信息。

class socketserver.StreamRequestHandler
class socketserver.DatagramRequestHandler
這兩個類是socketserver繼承BaseRequestHandler後重寫了setup(),finish(),實現了對讀,寫緩衝區的設置,有興趣的能夠看看源碼。

五、官方示例:

TCP同步服務示例:

# 服務端
import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):  # 自定義類,繼承BaseRequestHandler,處理socket請求

    def handle(self):  # socket客戶端請求
        self.data = self.request.recv(1024).strip()  # 接收socket客戶端發來的數據
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())  # 將數據大寫後發給客戶端
'''
class MyTCPHandler(socketserver.StreamRequestHandler):  
    # 自定義類,功能與上面的同樣,只不過是繼承StreamRequestHandler
    def handle(self):
        # self.rfile is a file-like object created by the handler;
        # we can now use e.g. readline() instead of raw recv() calls
        self.data = self.rfile.readline().strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        # Likewise, self.wfile is a file-like object used to write back
        # to the client
        self.wfile.write(self.data.upper())
'''

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999

    with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
        server.serve_forever()  # 啓用TCP服務器
# 打印內容以下
127.0.0.1 wrote:
b'Hello World'

# 客戶端
import socket
import sys

HOST, PORT = "localhost", 9999
data = "Hello World"

# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    # Connect to server and send data
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))

    # Receive data from the server and shut down
    received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))

# 打印內容以下
Sent:     Hello World
Received: HELLO WORLD

UDP服務示例:

# 服務端
import socketserver

class MyUDPHandler(socketserver.BaseRequestHandler):
    """
    This class works similar to the TCP handler class, except that
    self.request consists of a pair of data and client socket, and since
    there is no connection the client address must be given explicitly
    when sending data back via sendto().
    """

    def handle(self):
        data = self.request[0].strip()
        socket = self.request[1]
        print("{} wrote:".format(self.client_address[0]))
        print(data)
        socket.sendto(data.upper(), self.client_address)

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
        server.serve_forever()
# 打印內容以下
127.0.0.1 wrote:
b'Hello World'

# 客戶端
import socket
import sys

HOST, PORT = "localhost", 9999
data = "Hello World"

# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))
# 打印內容以下
Sent:     Hello World
Received: HELLO WORLD

 TCP服務異步示例:

import socket
import threading
import socketserver

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):  # 自定義socket請求處理類
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):  # 自定義線程類處理多個請求
    pass

def client(ip, port, message):
    '''
    socket客戶端
    :param ip: 服務段的IP地址
    :param port: 服務端的端口
    :param message: 給服務端發送的消息
    :return:
    '''
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":

    HOST, PORT = "localhost", 0  # 端口是0隨機獲取一個未被使用的端口
    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    with server:
        ip, port = server.server_address  # 獲取服務端的IP地址和端口號
        server_thread = threading.Thread(target=server.serve_forever) # 建立線程對象
        server_thread.daemon = True  # 守護線程
        server_thread.start()  # 開啓線程,在線程中開啓TCP服務器
        print("Server loop running in thread:", server_thread.name)
        # 模擬三個socket客戶端鏈接TCP服務器
        client(ip, port, "Hello World 1")
        client(ip, port, "Hello World 2")
        client(ip, port, "Hello World 3")

        server.shutdown()
# 打印內容以下:
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3

 

參考文檔:https://docs.python.org/3/library/socketserver.html?highlight=socketserver#module-socketserver

相關文章
相關標籤/搜索