1. 程式人生 > >Python實戰之SocketServer模塊

Python實戰之SocketServer模塊

utf8 mixin 程序 通過 框架 obj 基本使用 取數據 rgs

文章出處:http://www.cnblogs.com/wupeiqi/articles/5040823.html

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

ThreadingTCPServer

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

1、ThreadingTCPServer基礎

使用ThreadingTCPServer:

  • 創建一個繼承自 SocketServer.BaseRequestHandler 的類
  • 類中必須定義一個名稱為 handle 的方法
  • 啟動ThreadingTCPServer

內部調用流程為:

  • 啟動服務端程序
  • 執行 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方法)

import socket
import threading
import select def process(request, client_data): print(request, client_data) conn = request conn.sendall(bytes("Welcom to 10086",encoding=utf-8)) flag = True while flag: data = conn.recv(1024) if data == exit: flag = False elif data == 0: conn.sendall(bytes("0000",encoding=utf-8)) else: conn.sendall(bytes("Do it again",encoding="utf-8")) sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.bind((127.0.0.1,9999)) sk.listen(5) while True: r, w, e = select.select([sk,],[], [], 1) print(looping) if sk in r: print("Get request") request, client_address = sk.accept() t = threading.Thread(target=process, args=(request, client_address)) t.daemon = False t.start() sk.close()

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

ForkingTCPServer

ForkingTCPServer和ThreadingTCPServer的使用和執行流程基本一致,只不過在內部分別為請求者建立 “線程” 和 “進程”。

基本使用:

服務器端:

import socketserver

class MyServer(socketserver.BaseRequestHandler):

    def handle(self):
        # print(self.request, self.client_address, self.server)
        conn = self.request
        conn.sendall(bytes(Welcome to connect 10086,encoding=utf-8))
        Flag = True

        while Flag:
            data = conn.recv(1024)
            if data == exit:
                Flag = False
            elif data == 0:
                conn.sendall(bytes(sssssss,encoding=utf-8))
            else:
                conn.sendall(bytes(do it again,encoding=utf8))

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

客戶端:

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)
    print("Receive:",data)
    inp = input("Please input:")
    sk.sendall(inp)
    
    if inp == exit:
        break
sk.close()

事件驅動

簡而言之,事件驅動分為二個部分:第一,註冊事件;第二,觸發事件。

自定義事件驅動框架,命名為:“弒君者”:

event_list =[]

def run():
    for event in event_list:
        obj = event()
        obj.execute()

class BaseHandler(object):
    def execute(self):
        raise Exception("You must overwrite execute")

使用方法:

from day9 import source

class MyHandler(source.BaseHandler):
    def execute(self):
        print("Event-drive execute myhandler")
        
source.event_list.append(MyHandler)
source.run()

如上述代碼,事件驅動只不過是框架規定了執行順序,程序員在使用框架時,可以向原執行順序中註冊“事件”,從而在框架執行時可以出發已註冊的“事件”。

基於事件驅動Socket

from twisted.internet import protocol
from twisted.internet import reactor

class Echo(protocol.Protocol):
    def dataReceived(self, data):
        self.transport.write(data)
    

def main():
    factory = protocol.ServerFactory()
    factory.protocol = Echo
    
    reactor.listenTCP(8000, factory)
    reactor.run()

if __name__ == __main__:
    main()

程序執行流程:

  • 運行服務端程序
  • 創建Protocol的派生類Echo
  • 創建ServerFactory對象,並將Echo類封裝到其protocol字段中
  • 執行reactor的 listenTCP 方法,內部使用 tcp.Port 創建socket server對象,並將該對象添加到了 reactor的set類型的字段 _read 中
  • 執行reactor的 run 方法,內部執行 while 循環,並通過 select 來監視 _read 中文件描述符是否有變化,循環中...
  • 客戶端請求到達
  • 執行reactor的 _doReadOrWrite 方法,其內部通過反射調用 tcp.Port 類的 doRead 方法,內部 accept 客戶端連接並創建Server對象實例(用於封裝客戶端socket信息)和 創建 Echo 對象實例(用於處理請求) ,然後調用 Echo 對象實例的 makeConnection 方法,創建連接。
  • 執行 tcp.Server 類的 doRead 方法,讀取數據,
  • 執行 tcp.Server 類的 _dataReceived 方法,如果讀取數據內容為空(關閉鏈接),否則,出發 Echo 的 dataReceived 方法
  • 執行 Echo 的 dataReceived 方法

從源碼可以看出,上述實例本質上使用了事件驅動的方法 和 IO多路復用的機制來進行Socket的處理。

from twisted.internet import reactor, protocol
from twisted.web.client import getPage
from twisted.internet import reactor
import time

class Echo(protocol.Protocol):

    def dataReceived(self, data):
        deferred1 = getPage(http://cnblogs.com)
        deferred1.addCallback(self.printContents)

        deferred2 = getPage(http://baidu.com)
        deferred2.addCallback(self.printContents)

        for i in range(2):
            time.sleep(1)
            print execute ,i


    def execute(self,data):
        self.transport.write(data)

    def printContents(self,content):
        print len(content),content[0:100],time.time()

def main():

    factory = protocol.ServerFactory()
    factory.protocol = Echo

    reactor.listenTCP(8000,factory)
    reactor.run()

if __name__ == __main__:
    main()

Python實戰之SocketServer模塊