socketserver(多連接)


  正如前面的socket模塊部分看到的一樣,寫一個簡單套接字服務器不是很難,如果想實現超出繼承的應用,最好尋求一些幫助,socketserver模塊是標准庫中很多服務器框架的基礎,這些服務器架構包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer、DocXMLRPCServer,所有的這些服務器框架都為基礎服務器增加了特定功能;

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

 

ThreadingTCPServer(多線程,真並發

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

使用ThreadingTCPServer:

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

用socketserver對ssh程序做修改,實現多用戶同時操作互不影響

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 #-Author-Lian
 4 
 5 #scoketserver
 6 
 7 import socketserver,os
 8 
 9 class Myserver(socketserver.BaseRequestHandler):
10     def handle(self):
11         while True:
12             conn = self.request
13            # conn,add = server.accept()
14             while True:
15                 print("開始收")
16                 client_data = conn.recv(1024)
17                 client_data = client_data.decode("utf-8")
18                 if client_data == "exit":           #收到exit 退出
19                     break
20                 send_data = os.popen(client_data).read()    #執行命令結果,要發送的數據
21                 send_data = send_data.encode("utf-8")       #轉換為bytes類型
22 
23                 length = str(len(send_data))              #統計發送數據的長度
24                 conn.sendall(length.encode("utf-8"))      #長度以bytes類型發送過去
25 
26                 return_value = conn.recv(1024)
27                 return_value = return_value.decode("utf-8")
28 
29                 if return_value == "start":
30                     if not send_data:            # 如果執行結果為空,表示命令不存在
31                         conn.sendall((client_data +"命令不存在").encode("utf-8"))
32                     else:
33                       conn.sendall(send_data)
34             conn.close()
35 
36 if __name__ == '__main__':
37     server = socketserver.ThreadingTCPServer(("127.0.0.1",8888),Myserver)
38     server.serve_forever()
39 
40 ssh 服務端多用戶同時連接
ssh 服務端多用戶同時連接
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 #-Author-Lian
 4 
 5 #ssh client
 6 
 7 import socket
 8 
 9 ip_port = ("127.0.0.1",8888)
10 client = socket.socket()
11 client.connect(ip_port)
12 
13 while True:
14     cmd = input("->>").strip()
15     if not cmd:                 #空字符 重新輸入
16         continue
17     client.sendall(cmd.encode("utf-8"))     #要執行的命令發送過去
18     if cmd == "exit":           #如果為exit 退出連接
19         break
20 
21     length = client.recv(1024)     #數據長度
22     length = length.decode("utf-8")
23     length = int(length)            #長度轉換為int
24 
25     client.sendall("start".encode("utf-8"))     #發送字節start
26 
27     sum_data = ""                   #初始匯總的數據
28     while length >= 0:              #循環收數據
29         server_data = client.recv(1024)
30         length -=1024
31         sum_data +=server_data.decode("utf-8")
32     print(sum_data)                 #打印最終的執行數據
33 
34 client.close()
35 
36 ssh 客戶端多用戶同時連接
ssh 客戶端多用戶同時連接

 

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方法)

對源碼進行精簡做一個程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import  socket
import  threading
import  select
 
 
def  process(request, client_address):
     print  request,client_address
     conn  =  request
     conn.sendall( '歡迎致電 10086,請輸入1xxx,0轉人工服務.' )
     flag  =  True
     while  flag:
         data  =  conn.recv( 1024 )
         if  data  = =  'exit' :
             flag  =  False
         elif  data  = =  '0' :
             conn.sendall( '通過可能會被錄音.balabala一大推' )
         else :
             conn.sendall( '請重新輸入.' )
 
sk  =  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(( '127.0.0.1' , 8002 ))
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()
         =  threading.Thread(target = process, args = (request, client_address))
         t.daemon  =  False
         t.start()
 
sk.close()

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

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM