socketserver分類:服務器
一、TCP協議多線程
class socketserver.TCPServer(server_address,RequestHandlerClass,bind_and_activate=True)併發
二、UDP協議socket
class socketserver.UDPServer(server_address,RequestHandlerClass,bind_and_activate=True)ide
剩下兩種不經常使用協議以下:this
calss socketserver.UnixStreamServer(server_address,RequestHandlerClass,bind_and_activate=True)spa
class socketserver.UnixDatagramServer(server_adddress,RequestHandlerClass,bind_and_activate=True)線程
協議之間的繼承關係以下:3d
用socketServer建立一個服務的步驟:code
一、建立一個request handler class(請求處理類),合理選擇StreamRequestHandler和DatagramRequestHandler之中的一個做爲父類(固然,便用BaseRequestHandler做爲父類也可),並重寫它的handler()方法;
二、實例化一個server class對象,並將服務的地址和以前建立的request handler class傳遞給它;
三、調用server class對象的handle_request()或server_forver()方法來開始處理請求,server_forever()是永久處理鏈接,使用這一個。
handle_request()只處理一個請求,處理完一個請求以後退出;server_forever()處理多個請求。
與客戶端每個交互都是在handle()中處理的。
socketServer實際上是對客戶端功能的進一步封裝,好比bind(),listen(),accept()功能的封裝,把這些功能統一分裝到socketserver中,而且可以實現多線程,多進程的多併發的狀況,客戶端沒有變化,下面來看一個簡單的例子:
socketserver寫成的服務器端:
'''使用socketserver建立客戶端''' import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): '''建立一個socketserver服務器接收端,首先實例化一個類''' def handle(self): self.data = self.request.recv(1024).strip() #接收客戶端發送的消息 print("IP:%s,端口:%s連接進來了!!!" %(self.client_address[0],self.client_address[1])) '''打印那個客戶端端口鏈接過來了''' print(self.data.decode('utf-8')) #打印接收到的消息 self.request.send(self.data.upper()) #把接收到的消息大寫返回回去 if __name__ == "__main__": HOST,PORT = 'localhost',9999 server = socketserver.TCPServer((HOST,PORT),MyTCPHandler) '''建立鏈接實例''' server.serve_forever()
上面代碼中,咱們建立了socketserver實例的服務器端,其實原理與socket都是同樣的,不一樣的是,socketserver是對服務器端鏈接的封裝,如個別bind()、listen()、accept()的封裝,咱們只須要self.request.recv(length).strip()接收數據便可,不用關心太多底層的問題,讓建立socket的過程變得簡單。
socket建立的客戶端:
'''建立一個socketserver客戶端,客戶端與socket客戶端是徹底同樣的,只是服務器端不同而已''' import socket client = socket.socket() client.connect(("localhost",9999)) while True: mess = input("請輸入您要發送的數據>>:") client.send(mess.encode('utf-8')) #客戶端發送數據給服務器端 data = client.recv(1024) #客戶端接收服務器發送過來的數據 print("接收到的數據:",data.decode('utf-8'))
上面代碼是socket建立的客戶端,就是一個簡單的收發數據,在這裏可以連續發送數據,下面來首先啓動服務器端,接着啓動客戶端進行交互。
運行以下:
請輸入您要發送的數據>>:dfasfdasd
接收到的數據: DFASFDASD
請輸入您要發送的數據>>:1
接收到的數據:
請輸入您要發送的數據>>:das
Traceback (most recent call last):
File "/home/zhuzhu/第八天/socket_server/socketserver_client.py", line 8, in <module>
client.send(mess.encode('utf-8')) #客戶端發送數據給服務器端
BrokenPipeError: [Errno 32] Broken pipe
從上面能夠看出,第一次交互是沒有問題的,第二次交互就開始出錯了,爲何呢?咱們來看下服務器端,在服務器端裏面,handle()裏面每次只能接收一次數據,所以會出現錯誤的狀況,下面咱們來讓服務器端實現連續交互,修改服務器端,以下:
'''使用socketserver建立客戶端''' import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): '''建立一個socketserver服務器接收端,首先實例化一個類''' def handle(self): while True: self.data = self.request.recv(1024).strip() #接收客戶端發送的消息 if not self.data: '''若是接收的爲空,說明服務器端發送的是空的數據,或者是服務器端斷開了''' print(self.client_address,"客戶端斷開了!!!") break print("IP:%s,端口:%s連接進來了!!!" %(self.client_address[0],self.client_address[1])) '''打印那個客戶端端口鏈接過來了''' print(self.data.decode('utf-8')) #打印接收到的消息 self.request.send(self.data.upper()) #把接收到的消息大寫返回回去 if __name__ == "__main__": HOST,PORT = 'localhost',9997 server = socketserver.TCPServer((HOST,PORT),MyTCPHandler) '''建立鏈接實例''' server.serve_forever()
上面代碼中,對服務器進行了修改,讓服務器可以循環接收數據,這樣客戶端就能無限發送和接收數據了,不過要牢記一點,客戶端是不可以發送空的消息給服務器的,若是發送空的消息給服務器,就會卡主,傳遞不了消息,於是要設定發送空消息的指令。
上面代碼修改了,可以實現交互,可是咱們發現了一個問題,服務器仍是隻能每次跟一個客戶端交互,不能同時跟多個客戶端交互,其餘客戶端必須等待本次交互完成以後,在進行交互,這與socket寫的服務器和客戶端沒有區別,如何用socketserver實現多併發呢,下面有兩種方式,一種是多線程,以下:
(1)多線程
'''使用socketserver建立客戶端''' import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): '''建立一個socketserver服務器接收端,首先實例化一個類''' def handle(self): while True: self.data = self.request.recv(1024).strip() #接收客戶端發送的消息 if not self.data: '''若是接收的爲空,說明服務器端發送的是空的數據,或者是服務器端斷開了''' print(self.client_address,"客戶端斷開了!!!") break print("IP:%s,端口:%s連接進來了!!!" %(self.client_address[0],self.client_address[1])) '''打印那個客戶端端口鏈接過來了''' print(self.data.decode('utf-8')) #打印接收到的消息 self.request.send(self.data.upper()) #把接收到的消息大寫返回回去 if __name__ == "__main__": HOST,PORT = 'localhost',9997 server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler) '''建立鏈接實例''' server.serve_forever()
多線程就是修改server鏈接的方式,socketserver.ThreadingTCPServer便可,以下:
server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
上面修改以後,就能實現多線程多併發,咱們能夠同時啓動多個客戶端進行交互。
上面啓動一個服務器,多個客戶端,交互以下:
客戶端1:
請輸入您要發送的數據>>:這麼神奇
接收到的數據: 這麼神奇
客戶端2:
請輸入您要發送的數據>>:alex
接收到的數據: ALEX
客戶端3:
請輸入您要發送的數據>>:sb
接收到的數據: SB
客戶端4:
請輸入您要發送的數據>>:yes
接收到的數據: YES
服務器: IP:127.0.0.1,端口:54856連接進來了!!! 這麼神奇 IP:127.0.0.1,端口:54858連接進來了!!! alex IP:127.0.0.1,端口:54860連接進來了!!! sb IP:127.0.0.1,端口:54862連接進來了!!! yes
上面能夠看出,咱們實現了多線程多併發的交互,咱們也能夠實現多進程的交互。
(2)多進程
多進程和多線程是同樣的,也是修改服務器端的交互指令便可,以下:
server = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler)
上面,ForKingTCPServer((IP,端口號),實例化的類),就實現了和多線程一個的多併發狀況,以下:
'''使用socketserver建立客戶端''' import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): '''建立一個socketserver服務器接收端,首先實例化一個類''' def handle(self): while True: self.data = self.request.recv(1024).strip() #接收客戶端發送的消息 if not self.data: '''若是接收的爲空,說明服務器端發送的是空的數據,或者是服務器端斷開了''' print(self.client_address,"客戶端斷開了!!!") break print("IP:%s,端口:%s連接進來了!!!" %(self.client_address[0],self.client_address[1])) '''打印那個客戶端端口鏈接過來了''' print(self.data.decode('utf-8')) #打印接收到的消息 self.request.send(self.data.upper()) #把接收到的消息大寫返回回去 if __name__ == "__main__": HOST,PORT = 'localhost',9997 server = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler) '''建立鏈接實例''' server.serve_forever()
交互以下:
客戶端1: 請輸入您要發送的數據>>:dfasfdas 接收到的數據: DFASFDAS 客戶端2: 請輸入您要發送的數據>>:xiaozhuzhu 接收到的數據: XIAOZHUZHU 客戶端3: 請輸入您要發送的數據>>:pangshini 接收到的數據: PANGSHINI 客戶端4: 請輸入您要發送的數據>>:dapagnzhi 接收到的數據: DAPAGNZHI 服務器: IP:127.0.0.1,端口:54876連接進來了!!! dfasfdas IP:127.0.0.1,端口:54878連接進來了!!! xiaozhuzhu IP:127.0.0.1,端口:54880連接進來了!!! pangshini IP:127.0.0.1,端口:54886連接進來了!!! dapagnzhi
上面就是多進程下的多併發的交互狀況,和多線程交互結果是一致的,不一樣的是,ForKingTCPServer()多進程在Windows下會報錯,因爲Windows多進程和Linux多進程的原理是不同的。在Linux下沒有問題,在Windows下會報錯。
基本的socketserver代碼:
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): """ The request handler class for our server. It is instantiated once per connection to the server, and must override the handle() method to implement communication to the client. """ def handle(self): # self.request is the TCP socket connected to the client self.data = self.request.recv(1024).strip() print("{} wrote:".format(self.client_address[0])) print(self.data) # just send back the same data, but upper-cased self.request.sendall(self.data.upper()) if __name__ == "__main__": HOST, PORT = "localhost", 9999 # Create the server, binding to localhost on port 9999 server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # Activate the server; this will keep running until you # interrupt the program with Ctrl-C server.serve_forever()
總結:
(1)socketserver與socket是同樣的,都是爲了實現客戶端和服務器端的交互;
(2)socketserver可以實現多併發,而socket只能一對一的進行交互;
(3)socketserver.TCPServer是實現與socket同樣的一對一交互,socketserver.ThreadingTCPServer是實現多線程下的多併發,socketserver.ForkingTCPServer是實現多進程的下的多併發的數據交互,都是在TCP協議下進行交互的,經常使用的還有UDP協議。
(4)客戶端不能發送空的消息,服務器端也不能接收空的消息;
(5)若是服務器端接收的消息爲空,說明客戶端已經退出了,否則若客戶端發送爲空,則形成堵塞;
(6)socketserver是對socket中bind,listen,accept()的封裝。