正如前面的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 服務端多用戶同時連接

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 客戶端多用戶同時連接
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()
t
=
threading.Thread(target
=
process, args
=
(request, client_address))
t.daemon
=
False
t.start()
sk.close()
|
如精簡代碼可以看出,SocketServer的ThreadingTCPServer之所以可以同時處理請求得益於 select 和 Threading 兩個東西,其實本質上就是在服務器端為每一個客戶端創建一個線程,當前線程用來處理對應客戶端的請求,所以,可以支持同時n個客戶端鏈接(長連接)