利用python的socket模塊可以實現基本的網絡編程,並且只限於一對一的連接。當然,也可以在其基礎上實現一個網絡服務器,但由於太底層這種做法不被推薦。其實如果要實現一個網絡服務器很簡單,調用python的內置模塊socketserver就夠了。
server類
socketserver模塊下面有四種套接字server類:TCPserver
, UDPServer
, UnixStreamServer
, UnixDatagramServer
。前兩種分別為使用TCP和UDP協議的server類,后兩種用法和前面一樣但只限於unix類系統。它們的參數都一樣,如下:
TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
- 參數server_address: IP地址和端口,為一個元組如: ("127.0.0.1", 8000)
- 參數RequestHandlerClass: 一個自定義的handle類,主要負責實現連接到來時所要執行的操作。后面會介紹怎么自定義。
- 參數bind_and_activate: 默認為True。如果設置為False,代表你得手動操作底層的socket,這樣會更加靈活。
前面的四種類都繼承自一個BaseServer類,實現了基本方法和屬性:
- fileno(): 返回一個服務器正綁定的socket文件描述符。
- handle_requeset(): 處理一個請求。依次執行 get_request(), verify_request(), 和 process_request() 方法。用戶自定義的handleclass下的handle方法拋出異常,服務器的handle_error()會被調用。
- server_forever(poll_interval=0.5): 進入一個循環, 一直接收並處理請求直到一個顯示的 shutdown() 請求到來。默認每隔0.5秒輪詢(pull)一次。
- shutdown(): 告訴server_forever()得到的循環結束循環。
- server_address(): 返回正在監聽的IP和端口,如:("127.0.0.1", 80)
- socket: 正使用的socket對象
- socket_type: socket類型,通常為:socket.SOCK_STREAM和socket.SOCK_DGRAM
- timeout: 超時時間。
請求處理類
通常需要繼承BaseRequestHandler
,並重寫hanle()方法。當一個網絡請求被創建時,一個新的實例就會被創建。
class socketserver.BaseRequestHandler
方法如下:
- setup(): 在handle()被調用前被執行,一般用於一些初始化操作。默認不執行任何操作。
- handle(): 當一個請求到來后,用戶所要執行操作,這個方法應該被重寫,操作self.request。
- finish(): handle之后調用的函數,用於執行一個清理工作。
server代碼如下:
1 import socketserver 2 3 class echorequestserver(socketserver.BaseRequestHandler): 4 def handle(self): 5 print('服務端啟動...') 6 conn = self.request 7 print('獲得連接:', self.client_address) 8 while True: 9 client_data = conn.recv(1024) 10 if not client_data: 11 print('斷開連接') 12 break 13 print(client_data.decode('utf-8')) 14 print('開始發送...') 15 conn.sendall(client_data) 16 17 if __name__ == '__main__': 18 server =socketserver.TCPServer(("127.0.0.1", 8000),echorequestserver) # 使用處理單連接的TCPServer 19 server.serve_forever()
客戶端代碼如下:
1 import socket,time 2 3 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 s.connect(('127.0.0.1',8000)) 5 6 while True: 7 st = input('input command: ') 8 if not st:break 9 s.send(st.encode('utf-8')) 10 11 echo_back = s.recv(1024) 12 print(echo_back.decode('utf-8')) 13 14 s.close()
當然,以上例子是用於單線程的情況。如果想同時接收多個連接,可以換成ThreadingTCPServer,用法和上面完全一樣。不過這種情況下,建議使用線程池以避免突然面臨多個連接同時到來的情況。