socketserver模塊

  雖然說用Python編寫簡單的網絡程序很方便,但複雜一點的網絡程序仍是用現成的框架比較好。這樣就能夠專心事務邏輯,而不是套接字的各類細節。SocketServer模塊簡化了編寫網絡服務程序的任務。同時SocketServer模塊也是Python標準庫中不少服務器框架的基礎python

  socketserver模塊能夠簡化網絡服務器的編寫,Python把網絡服務抽象成兩個主要的類,一個是Server類,用於處理鏈接相關的網絡操做,另一個則是RequestHandler類,用於處理數據相關的操做。而且提供兩個MixIn 類,用於擴展 Server,實現多進程或多線程。服務器

1、單線程示例

  server端和客戶端持續聊天的示例,可是同時只能和一個客戶端進行通訊,並無併發的效果。網絡

一、服務端代碼

from socket import *
ip_port=('127.0.0.1',8880)

tcp_socket_server=socket()
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

while 1:

    conn,addr=tcp_socket_server.accept()
    print('客戶端',addr)
    while 1:
        client_data=conn.recv(1024)
        if len(client_data)==0:
              print("客戶端斷開鏈接,等待新的用戶鏈接....")
              break
        print ("接受數據 >>>",str(client_data,"utf8"))
        response=input("響應數據 >>>")
        conn.sendall(bytes(response,"utf8"))

    conn.close()

二、客戶端代碼

import socket
ip_port = ('127.0.0.1',8888)
sock = socket.socket()
sock.connect(ip_port)
print ("客戶端啓動:")

while True:

    inp = input('發送數據 >>>')
    if inp == 'exit':
        break
    sock.sendall(bytes(inp,"utf8"))
    server_response=sock.recv(1024)
    print ("服務端響應數據 >>>",str(server_response,"utf8"))

sock.close()

2、socketserver的使用模式

import socketserver

class MyServer(socketserver.BaseRequestHandler):    # 必須繼承這個類
    def handle(self):
        """
        併發的業務邏輯
        conn(客戶端套接字對象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if client_data.decode("utf8") == "exit":
                  print("客戶端斷開鏈接,等待新的用戶鏈接....")
                  break
            print("接受數據 >>>",str(client_data, "utf8"))
            response=input("響應數據 >>>")
            self.request.sendall(bytes(response, "utf8"))

        self.request.close()

# 封裝TCP協議相關的套接字對象
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一個參數是ip+port,第二個參數是類名MyServer

server.serve_forever()

一、總結socketserver使用分如下三步:

(1)自定義功能類

class MyServer(socketserver.BaseRequestHandler):    # 必須繼承這個類
    def handle(self):
        """
        併發的業務邏輯
        conn(客戶端套接字對象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if len(client_data)==0:
                  print("客戶端斷開鏈接,等待新的用戶鏈接....")
                  break
            print ("接受數據 >>>",str(client_data,"utf8"))
            response=input("響應數據 >>>")
            self.request.sendall(bytes(response,"utf8"))

        self.request.close()

(2)封裝TCP協議相關的套接字對象

server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer)   # 第一個參數是ip+port,第二個參數是類名MyServer

(3)調用server.serve_forever()

server.serve_forever()

二、運行客戶端服務端顯示效果

        

3、socketserver源碼解析

一、socketserver.ThreadingTCPServer源碼解析

(1)查看ThreadingTCPServer類

class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

  在socketserver.py中查看到這句源碼,混合繼承了ThreadingMixIn和TCPServer類。根據廣度優先先查看左邊的類。多線程

(2)查看ThreadingMixIn類

class ThreadingMixIn:
    """Mix-in class to handle each request in a new thread."""

    # Decides how threads will act upon termination of the
    # main process
    daemon_threads = False

    def process_request_thread(self, request, client_address):...

    def process_request(self, request, client_address):...

  發現這個類並無__init__方法,只有兩個實例方法。所以再回去查看TCPServer類。併發

(3)查看TCPServer類中的__init__方法

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):
        """
        構造函數
        :param server_address: 接收的要綁定的元組('127.0.0.1', 8880)
        :param RequestHandlerClass: 接收功能類MyServer
        :param bind_and_activate:
        """
        BaseServer.__init__(self, server_address, RequestHandlerClass)   # 執行BaseServer的__init__方法
        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
    """後面代碼省略"""

  注意__init__方法接收的參數,server_address: 接收的要綁定的元組('127.0.0.1', 8880);RequestHandlerClass: 接收功能類MyServer。框架

  __init__方法第一步就是執行BaseServer的__init__方法。socket

(4)查看BaseServer的__init__方法

class BaseServer:
    timeout = None

    def __init__(self, server_address, RequestHandlerClass):
        """Constructor.  May be extended, do not override."""
        self.server_address = server_address   # 實例變量賦值
        self.RequestHandlerClass = RequestHandlerClass   # 實例變量賦值
        self.__is_shut_down = threading.Event()
        self.__shutdown_request = False

    """代碼省略"""

  能夠看到主要是作了一個實例變量賦值tcp

(5)繼續解析TCPServer類中的__init__方法

  在執行完BaseServer的__init__方法後,接下里開始建立socket對象:ide

self.socket = socket.socket(self.address_family,
                            self.socket_type)

  socket對象建立完成後,執行socket.bind和socket.listen方法:函數

class TCPServer(BaseServer):
    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise

    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)   # bind元組地址
        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)   # listen,這裏默認值也是5

(6)總結server = socketserver.ThreadingTCPServer(('127.0.0.1', 8880), MyServer) 作的事情

  1.self.socket  2.self.socket.bind()  3.self.socket.listen(5) 

  這三步完成後,初始化也就完成了。

二、server.serve_forever()源碼解析

  因爲server是ThreadingTCPServer的實例對象,所以依次追溯ThreadingTCPServer、ThreadingMixIn、TCPServer、BaseServer類,最終在BaseServer類中找到serve_forever方法。

(1)查看serve_forever方法

class BaseServer:
    def serve_forever(self, poll_interval=0.5):
        self.__is_shut_down.clear()
        try:
            with _ServerSelector() as selector:   # IO多路複用監聽
                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()

  前面兩步都是監聽相關的操做,直接查看self._handle_request_noblock()源碼。

(2)查看_handle_request_noblock方法

  一樣是依次追溯各個類,最後在BaseServer類中找到方法:

class BaseServer:
    def _handle_request_noblock(self):
        try:
            request, client_address = self.get_request()
        except OSError:
            return
        if self.verify_request(request, client_address):
            try:
                self.process_request(request, client_address)
            except Exception:
                self.handle_error(request, client_address)
                self.shutdown_request(request)
            except:
                self.shutdown_request(request)
                raise
        else:
            self.shutdown_request(request)

(3)查看self.get_request()方法

class TCPServer(BaseServer):

    def get_request(self):
        """Get the request and client address from the socket.
        May be overridden.
        """
        return self.socket.accept()

  在這裏找到了socket.accept方法,被動接受TCP客戶的鏈接,(阻塞式)等待鏈接到來。

  所以_handle_request_noblock中的request, client_address = self.get_request():request就是新的套接字對象(conn),能夠用來接收和發送數據;client_address就是鏈接客戶端的地址

  繼續查看_handle_request_noblock方法的self.process_request(request, client_address)調用。

(4)查看process_request方法

  這裏按照查找順序,在ThreadingMixIn類找到process_request方法。(注意不要找BaseServer裏的process_request,它已經被覆蓋了)

class ThreadingMixIn:

    def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        t = threading.Thread(target = self.process_request_thread,
                             args = (request, client_address))
        t.daemon = self.daemon_threads
        t.start()

  每來一個用戶就開啓一個線程,執行self.process_request_thread(request, client_address)方法。

  process_request_thread方法也在ThreadingMixIn類中。

(5)查看process_request_thread方法

class ThreadingMixIn:
    def process_request_thread(self, request, client_address):
        try:
            self.finish_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
        finally:
            self.shutdown_request(request)

  按照查找順序在BaseServer中找到finish_request方法。

(6)查看finish_request方法

class BaseServer:
    def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)

  注意這裏的RequestHandlerClass在TCPServer類中的__init__方法執行時,用RequestHandlerClass來接收功能類MyServer。

  這裏是在調用MyServer類的實例化。

class MyServer(socketserver.BaseRequestHandler):    # 必須繼承這個類
    def handle(self):
        """
        併發的業務邏輯
        conn(客戶端套接字對象):self.request
        :return:
        """
        while 1:
            client_data=self.request.recv(1024)
            if client_data.decode("utf8") == "exit":
                  print("客戶端斷開鏈接,等待新的用戶鏈接....")
                  break
            print("接受數據 >>>",str(client_data, "utf8"))
            response=input("響應數據 >>>")
            self.request.sendall(bytes(response, "utf8"))

        self.request.close()

  可是這個類並無__init__方法,所以須要查看它的父類__init__方法。

(7)查看BaseRequestHandler類

class BaseRequestHandler:

    def __init__(self, request, client_address, server):
        self.request = request     # 新的套接字對象(conn)實例變量賦值
        self.client_address = client_address    # 鏈接客戶端的地址實例變量賦值
        self.server = server
        self.setup()
        try:
            self.handle()  # 調用本身的handle方法
        finally:
            self.finish()

  在完成新的套接字對象(conn)實例變量賦值和鏈接客戶端的地址實例變量賦值後,調用本身的handle方法。

  這個self.handle()調用的不是BaseRequestHandler類的handle方法而是MyServer的handle方法,執行自定義的handle方法

三、流程圖

  

相關文章
相關標籤/搜索