Python中的Socket編程,全掌握!
在如今的互聯網當中,Socket 協議是最重要的基礎之一。本文涵蓋了在 Python 中處理 Socket 編程的所有領域。
為什么使用 Sockets
Sockets 是組成當今網絡的各種通信協議,這些協議使得在兩個不同的程序或設備之間傳輸信息成為可能。例如,當我們打開瀏覽器時,我們作為客戶機就會創建與服務器的連接以傳輸信息。
在深入研究這個通信原理之前,讓我們先弄清楚 Sockets 到底是什么。
什么是 Sockets
一般來說,Socket 是為發送和接收數據而構建的內部應用協議。單個網絡將有兩個 Sockets,每個 Sockets 用于通信設備或程序,這些 Sockets 是IP地址和端口的組合。根據使用的端口號,單個設備可以有“n”個 Sockets,不同的端口可用于不同類型的協議。
下圖展示了一些常見端口號和相關協議的信息:
協議 | 端口號 | Python 庫 | 應用 |
HTTP | 80 | httplib,urllib,requests | 網頁,網站 |
FTP | 20 | ftplib | 文件傳輸 |
NNTP | 119 | nttplib | 新聞傳輸 |
SMTP | 25 | smtplib | 發送郵件 |
Telnet | 23 | telnetlib | 命令行 |
POP3 | 110 | poplib | 接收郵件 |
Gopher | 70 | gopherlib | 文檔傳輸 |
現在我們已經了解了 Sockets 的概念,現在讓我們來看看 Python 的 Socket 模塊
如何在 Python 中實現 Socket 編程
要在 Python 中實現 Socket 編程,需要導入 socket 模塊。
該模塊的一些重要方法如下:
方法 | 描述 |
socket.socket() | 用于創建 socket(服務器端和客戶端都需要創建) |
socket.accept() | 用于接受連接。它返回一對值(conn,address),其中conn是用于發送或接收數據的新 socket 對象,address是連接另一端的 socket 地址 |
socket.bind() | 用于綁定到指定為參數的地址 |
socket.close() | 用于關閉 socket |
socket.connect() | 用于連接到指定為參數的遠程地址 |
socket.listen() | 使服務器能夠接受連接 |
現在我們已經了解了 socket 模塊的重要性,接下來讓我們看看如何在 Python 中建服務器和客戶機。
什么是服務器
服務器或者是一個程序、一臺計算機,或者是一臺專門用于管理網絡資源的設備。服務器可以位于同一設備或計算機上,也可以本地連接到其他設備和計算機,甚至可以遠程連接。有各種類型的服務器,如數據庫服務器、網絡服務器、打印服務器等。
服務器通常使用socket.socket(),socket.bind(),socket.listen()等來建立連接并綁定到客戶端,現在讓我們編寫一個程序來創建服務器。
創建 socket 的第一個必要條件是導入相關模塊。之后是使用socket.socket()方法創建服務器端 socket。
AF_INET 是指來自 Internet 的地址,它需要一對(主機、端口),其中主機可以是某個特定網站的 URL 或其地址,端口號為整數。SOCK_STREAM 用于創建 TCP 協議。
bind()?方法接受兩個參數作為元組(主機、端口)。這里需要注意的是最好使用4位的端口號,因為較低的端口號通常被占用或者是系統預留的。listen()方法允許服務器接受連接,5是同時接受的多個連接的隊列。此處可以指定的最小值為0,如果未指定參數,則采用默認的合適參數。
while?循環允許永遠接受連接,clt和adr?是客戶端對象和地址,print?語句只是打印出客戶端 socket 的地址和端口號,最后,clt.send用于以字節為單位發送數據。
現在我們的服務器已經設置好了,讓我們繼續向客戶機前進。
什么是客戶端
客戶端是從服務器接收信息或服務的計算機或軟件。在客戶端-服務器模型中,客戶端從服務器請求服務。最好的例子是 Google Chrome、Firefox 等 Web 瀏覽器,這些 Web 瀏覽器根據用戶的指示請求 Web 服務器提供所需的網頁和服務。其他示例包括在線游戲、在線聊天等。
現在,讓我們看看如何用 Python 編程語言編寫客戶端程序:
首先依然是導入 socket 模塊,然后像創建服務器時那樣創建套接字。接著要在客戶端服務器之間創建連接,需要通過指定(host,port)使用 connect()方法。
注意:當客戶端和服務器位于同一臺計算機上時,需要使用gethostname。(LAN–localip/WAN–publicip)
在這里,客戶端希望從服務器接收一些信息,為此,我們需要使用recv()?方法,信息存儲在另一個變量msg中。需要注意的是正在傳遞的信息將以字節為單位,在上述程序的客戶端中,一次傳輸最多可以接收1024字節(緩沖區大小)。根據傳輸的信息量,可以將其指定為任意數量。
最后,再解碼并打印正在傳輸的消息。
現在我們已經了解了如何創建客戶端-服務器程序,接下來讓我們看看它們需要如何執行。
客戶端服務器交互
要執行這些程序,需要打開命令程序,進入創建客戶端和服務器程序的文件夾,然后鍵入:
不出意外服務器開始運行
要執行客戶端,需要打開另一個cmd窗口,然后鍵入:
下面讓我們將緩沖區大小減少到7,來看看相同的程序會怎么樣
如圖所示,傳輸7個字節后,連接終止。
其實這是一個問題,因為我們尚未收到完整的信息,但是連接卻提前關閉了,下面讓我們來解決這個問題。
多重通信
為了在客戶端收到完整信息之前繼續連接,我們可以使用while循環
如此修改之后,每次傳輸將以7個字節接收完整消息。
但這又引來了另一個問題,連接永遠不會終止,你永遠不知道什么時候會終止。此外,如果我們實際上不知道客戶端將從服務器接收到的消息或信息有多大,該怎么辦。在這種情況下,我們需要繼續完善代碼
在服務器端,使用close()方法,如下所示:
輸出如下:
程序會檢查信息的大小,并將其打印到一次兩個字節的緩沖區中,然后在完成連接后關閉連接。
傳輸 Python 對象
目前為止我們僅僅掌握了傳遞字符串的方法,但是,Python 中的 Socket 編程也允許我們傳輸 Python 對象。這些對象可以是集合、元組、字典等。要實現這一點,需要用到 Python 的 pickle 模塊。
Python pickle模塊
當我們實際序列化或反序列化 Python 中的對象時,就會使用到 Python pickle 模塊。讓我們看一個小例子
Output:
在上面的程序中,mylist?是使用pickle模塊的dumps()?函數序列化的。還要注意,輸出以b開頭,表示它已轉換為字節。在 socket 編程中,可以實現此模塊以在客戶端和服務器之間傳輸 python 對象。
如何使用 pickle 模塊傳輸 Python 對象
當我們將 pickle 與 socket 一起使用時,完全可以通過網絡傳輸任何內容。
先來看看服務端代碼
Server-Side:
這里,m?是一個字典,它基本上是一個需要從服務器發送到客戶端的 Python 對象。這是通過首先使用dumps()序列化對象,然后將其轉換為字節來完成的。
現在,讓我們記下客戶端:
Client-Side:
第一個while循環將幫助我們跟蹤完整的消息(complete_info)以及正在使用緩沖區接收的消息(rec_msg)。
然后,在接收消息時,我們所做的就是打印每一位消息,并將其放在大小為10的緩沖區中接收。此大小可以是任何大小,具體取決于個人選擇。
然后如果收到的消息等于完整消息,我們只會將消息打印為收到的完整信息,然后使用loads()反序列化消息。
輸出如下:
? ?