03, iLoRa Layer 3- Socket層
內部測試中,release soon…
Python 中的 socket 程式
在所有具有 socket 的語言中,socket 都是相同的 —— 這是兩個應用程式彼此進行通訊的導管。
使用 Python 這種具有 socket 程式功能的語言的區別在於,它有一些輔助的類和方法,可以簡化 socket 程式。在本節中,我們將展示 Python 的socket
API。可以使用一個程式檔來執行 Python 的譯程式,如果您要自己執行 Python,就可以一次只匯入一行程式碼。這樣,就可以看到每個方法呼叫之后的結果了。
下面這個例子展示了如何與 Python 譯程式進行互動。此處我們使用了 socket
類方法 gethostbyname
將一個完整的域名(www.ibm.com)解析成一個使用點號分隔的 IP 位址符串(‘129.42.19.99’):
清單 3. 從譯程式指令行中使用 socket
|
在導入 socket
模組之后,我呼叫了 gethostbyname
類方法將這個域名解析成 IP 位址。
現在,我們要討論基本的 socket
方法,并通過 socket 進行通訊。您應該熟悉 Python 譯程式。
建立和銷毀 socket
要新建立一個 socket,可以使用 socket
類的 socket
方法。這是一個類方法,因為還沒有得到可以應用方法的 socket
物件。socket
方法與 BSD API 類似,下面是建立流(TCP) socket 和資料封包(UDP)socket 的方法:
|
在這種情況中,會傳回一個 socket
物件。AF_INET
象徵式(第一個參數)說明您正在要求的是一個 Internet 協定(IP) socket,具體來說是 IPv4。第二個參數是傳送協定的類別(SOCK_STREAM
對應 TCP socket,SOCK_DGRAM
對應 UDP socket)。如果底層作業系統支援 IPv6,那么還可以指定 socket.AF_INET6
來建立一個 IPv6 socket。
要關閉一個已經連線的 socket,可以使用 close
方法:
streamSock.
close
()
最后,可以使用 del
敘述移除一個 socket:
del streamSock
這個敘述可以永久地移除 socket
物件。之后再嘗試參照這個物件就會產生錯誤。
Socket 位址
socket 位址是一個組合,包括一個介面位址和一個通訊埠號。由於 Python 可以很簡單地表示元組,因此位址和通訊埠也可以這樣表示。下面表示的是介面位址 192.168.1.1 和通訊埠 80:
( '192.168.1.1', 80 )
也可以使用完整的域名,例如:
( 'www.ibm.com', 25 )
伺服器 socket
伺服器 socket 通常會在網路上提供一個服務。由於伺服器和用戶機的 socket 是使用不同的方式建立的,因此我們將分別進行討論。
在建立 socket 之后,可以使用 bind
方法來連結一個位址,listen
方法可以將其設定為監聽狀態,最后 accept
方法可以接收一個新的用戶機連線。下面展示了這種用法:
清單 5. 使用伺服器 socket
|
對於這個伺服器來說,使用位址 ('', 2525)
就意味着介面位址中使用了萬用字元 ('')
,這樣可以接收來自這個主機上的任何介面的連線。還說明要連結到通訊埠 2525 上。
注意此處 accept
方法不但要傳回一個新的 socket
物件,它表示了用戶機連線(newsock
);而且還要傳回一個位址對(socket 端的遠端位址和通訊埠號)。Python 的 SocketServer
模組可以對這個過程進一步進行簡化,正如上面展示的一樣。
雖然也可以建立資料封包伺服器,不過這是無連線的,因此沒有對應的 accept
方法。下面的例子建立一個資料封包伺服器 socket:
清單 6. 建立一個資料封包伺服器 socket
|
后面對於 socket I/O 的討論將說明 I/O 是如何為流 socket 和資料封包 socket 工作的。
現在,讓我們來看一下用戶機是如何建立 socket 并將其連線到伺服器上的。
用戶機 socket
用戶機 socket 的建立和連線機制與伺服器 socket 相似。在建立 socket 之前,都需要一個位址 —— 不是區域連結到這個 socket 上(就像伺服器 socket 的情況那樣),而是識別這個 socket 應該連線到什么地方。假設在這個主機的 IP 位址 ‘192.168.1.1’ 和通訊埠 2525 上有一個伺服器。下面的程式碼可以建立一個新的 socket,并將其連線到定義的伺服器上:
|
對於資料封包 socket 來說,處理過程稍有不同。回想一下,資料封包從本質上來說都是沒有連線的。可以這樣考慮:流 socket 是兩個點之間的通訊導管,而資料封包 socket 是基於訊息的,可以同時與多個點進行通訊。下面是一個資料封包用戶機的例子。
清單 8. 建立一個資料封包 socket 并將其連線到伺服器上
|
儘管我們使用了 connect
方法,但是此處是有區別的:在用戶機和伺服器之間并不存在真正的 連線。此處的連線是對以后 I/O 的一個簡化。通常在資料封包 socket 中,必須在所傳送的資料中提供目標位址的資訊。通過使用 connect
,我們可以使用用戶機對這些資訊進行緩衝區,并且 send
方法的使用可以與流 socket 情況一樣(只不過不需要目標位址)。可以再次呼叫 connect
來重新指定資料封包用戶機訊息的目標。
socket I/O
通過流 socket 傳送和接收資料在 Python 中是很簡單的。有几個方法可以用來通過流 socket 傳遞資料(例如 send
、recv
、read
和 write
)。
第一個例子展示了流 socket 的伺服器和用戶機。在這個例子中,伺服器會回顯從用戶機接收到的資訊。
回顯流伺服器如清單 9 所示。在建立一個新的流 socket 之前,需要先連結一個位址(接收來自任何介面和 45000 通訊埠的連線),然后呼叫 listen
方法來啟用到達的連線。這個回顯伺服器然后就可以迴圈處理各個用戶機連線了。它會呼叫 accept
方法并阻塞(即不會傳回),直到有新的用戶機連線到它為止,此時會傳回新的用戶機 socket,以及遠端用戶機的位址資訊。使用這個新的用戶機 socket,我們可以呼叫 recv
來從另一端接收一個符串,然后將這個符串寫回這個 socket。然后立即關閉這個 socket。
清單 9. 簡單的 Python 流回顯伺服器
|
清單 10 察看了與清單 9 的回顯伺服器對應的用戶機。在建立一個新的流程 socket 之前,需要使用 connect
方法將這個 socket 連線到伺服器上。當連線之后(connect
方法傳回),用戶機就會使用 send
方法匯出一條簡單的字檔訊息,然后使用 recv
方法等待回顯。print
敘述用來察看所讀取的內容。當這個過程完成之后,就執行 close
方法關閉 socket。
清單 10. 簡單的 Python 流回顯用戶機
|
資料封包 socket I/O
資料封包 socket 天生就是無連線的,這意味着通訊需要提供一個目標位址。類似,當通過一個 socket 接收訊息時,必須同時傳回資料源。recvfrom
和 sendto
方法可以支援其他位址,正如您在資料封包回顯伺服器和用戶機實現中可以看到的一樣。
清單 11 察看了資料封包回顯伺服器的程式碼。首先建立一個 socket,然后使用 bind
方法連結到一個位址上。然后進入一個無限迴圈來處理用戶機的要求。recvfrom
方法從一個資料封包 socket 接收訊息,并傳回這個訊息以及發出訊息的源位址。這些資訊然后會被傳入 sendto
方法,將這些訊息傳回到源端。
清單 11. 簡單的 Python 資料封包回顯伺服器
|
資料封包用戶機更加簡單。在建立資料封包 socket 之后,我們使用 sendto
方法將一條訊息傳送到一個指定的位址。(記住:資料封包是無連線的。)在 sendto
完成之后,我們使用 recv
來等待回顯的回應,然后列印所收到的資訊。注意此處我們并沒有使用 recvfrom
,這是因為我們對兩端的位址資訊并不感興趣。
清單 12. 簡單的 Python 資料封包回顯用戶機
|
socket 選項
socket 在預設情況下有一些標准的行為,但是可以使用一些選項來修改 socket 的行為。我們可以使用 setsockopt
方法來修改 socket 的選項,并使用 getsockopt
方法來讀取 socket 選項的值。
在 Python 中使用 socket 選項非常簡單,正如清單 13 所示。在第一個例子中,我們讀取的是 socket 傳送緩衝區的大小。在第二個例子中,我們抓取 SO_REUSEADDR
選項的值(重用 TIME_WAIT
中的位址),然后來啟用它。
清單 13. 使用 socket 選項
|
SO_REUSEADDR
選項通常是在 socket 伺服器的開發中使用的。可以增大 socket 的傳送和接收緩衝區,從而獲得更好的效能,但是記住您是在一個解譯程式檔中進行作業的,因此可能不會帶來太多益處。
非同步 I/O
Python 作為 select
模組的一部分提供了非同步 I/O 的功能。這種特徴與 C 的 select 機制類似,但是更加簡單。我們首先對 select
進行簡介,然后解譯如何在 Python 中使用。
select
方法允許對多個 socket 產生多個事件或多個不同的事件。例如,您可以告訴 select
當 socket 上有資料可用時、當可以通過一個 socket 寫入資料時以及在 socket 上發生錯誤時,都要通知您;可以同時為多個 socket 執行這些作業。
在 C 使用點陣圖的地方,Python 使用清單來表示要監視的說明符,并且傳回那些滿足約束條件的說明符。在下面的例子中,等待從標准匯入裝置上匯入資訊:
清單 14. 等待 stdin 的匯入
|
傳遞給 select
的參數是几個清單,分別表示讀事件、寫事件和錯誤事件。select
方法傳回三個清單,其中包含滿足條件的物件(讀、寫和不規則)。在這個例子中,傳回的 rlist
應該是 [sys.stdin]
,說明資料在 stdin 上可用了。然后就可以使用 read
方法來讀取這些資料。
select
方法也可以處理 socket 說明符。在下面的例子(請參閱清單 15)中,我們建立了兩個用戶機 socket,并將其連線到一個遠端端上。然后使用 select
方法來確定哪個 socket 可以讀取資料了。接着可以讀取這些資料,并將其察看到 stdout 上。
清單 15. 展示處理多個 socket 的 select 方法
|