Socket通信¶
概要¶
socket幾乎是整個網絡通信的基礎,本文為大家講解Micropython中的Socket模塊。
socket模塊 API文檔¶
宏¶
socket
模塊中定義了許多和協議相關的宏,筆者整理出了下表:
宏定義名稱 | 值(int) | 功能 | 含義 |
---|---|---|---|
socket.AF_INET |
2 | 地址簇 | TCP/IP – IPv4 |
socket.AF_INET |
10 | 地址簇 | TCP/IP - IPv6 |
socket.SOCK_STREAM |
1 | 套接字類型 | TCP流 |
socket.SOCK_DGRAM |
2 | 套接字類型 | UDP數據報 |
socket.SOCK_RAW |
3 | 套接字類型 | 原始套接字 |
socket.SO_REUSEADDR |
4 | 套接字類型 | socket可重用 |
socket.IPPROTO_TCP |
16 | IP協議號 | TCP協議 |
socket.IPPROTO_UDP |
17 | IP協議號 | UDP協議 |
socket.SOL_SOCKET |
4095 | 套接字選項級別 |
函數¶
socket.getaddrinfo(host, port)¶
函數說明:將主機域名(host)和端口(port)轉換為用于創建套接字的5元組序列。元組列表的結構如下:
(family, type, proto, canonname, sockaddr)
示例:
>>> info = socket.getaddrinfo("127.0.0.1", 10000) >>> print(info) [(2, 1, 0, '127.0.0.1', ('127.0.0.1', 10000))]
socket.socket([af, type, proto])¶
函數說明:創建套接字。
-
af
:地址 -
type
:類型 -
proto
:協議號
*注意: 一般不指定proto參數,因為有些Micropython固件提供默認參數。 *
示例:
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) >>> print(s) <socket>
socket.socket.setsockopt(level, optname, value)¶
函數說明:根據選項值設置套接字。
-
level
:套接字選項級別 -
optname
:套接字的選項 -
value
:可以是一個整數,也可以是一個表示緩沖區的bytes類對象。
示例:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
socket.socket.bind(address)¶
函數說明:以列表或元組的方式綁定地址和端口號。
address
:一個包含地址和端口號的列表或元組。
示例:
addr = ("127.0.0.1",10000) s.bind(addr)
socket.socket.listen([backlog])¶
函數說明:監聽套接字,使服務器能夠接收連接。
backlog
:接受套接字的最大個數,至少為0,如果沒有指定,則默認一個合理值。
socket.socket.accept()¶
函數說明:接收連接請求。
注意:只能在綁定地址端口號和監聽后調用,返回conn和address。
-
conn:新的套接字對象,可以用來收發消息
-
address:連接到服務器的客戶端地址
示例:
conn,addr = s.accept()
socket.socket.connect(address)¶
函數說明:連接服務器。
- address:服務器地址和端口號的元組或列表
示例:
host = "192.168.3.147" port = 100 s.connect((host, port))
socket.socket.send(bytes)¶
函數說明:發送數據,并返回發送的字節數。
- bytes:bytes類型數據
示例:
s.send("hello 1ZLAB, I am TCP Client")
socket.socket.sendall(bytes)¶
函數說明:與send()函數類似,區別是sendall()函數通過數據塊連續發送數據。
- bytes:bytes類型數據
示例:
s.sendall("hello 1ZLAB, I am TCP Client")
socket.socket.sendto(bytes, address)¶
函數說明:發送數據,目標由address決定,用于UDP通信,返回發送的數據大小。
-
bytes:bytes類型數據
-
address:目標地址和端口號的元組
示例:
data = sendto("hello 1ZLAB", ("192.168.3.147", 100))
socket.socket.recv(bufsize)¶
函數說明:接收數據,返回接收到的數據對象。
- bufsize:指定一次接收的最大數據量
示例:
data = conn.recv(1024)
socket.socket.recvfrom(bufsize)¶
函數說明:接收數據,用于UDP通信,并返回接收到的數據對象和對象的地址。
- bufsize:指定一次接收的最大數據量
示例:
data,addr=fd.recvfrom(1024)
socket.socket.settimeout(value)¶
函數說明:設置超時時間,單位:秒。
示例:
s.settimeout(2)
socket.socket.readline()¶
函數說明:接收一行數據,遇換行符結束,并返回接收數據的對象 。
socket.socket.write(buf)¶
函數說明:將字節類型數據寫入套接字,并返回寫入數據的大小。
socket.socket.close()¶
函數說明:關閉套接字。
示例:
s.close()
TCP服務端和客戶端通信示例¶
確保你的ESP32和你的PC在同一局域網內。
在接下來的示例中,我們以ESP32建立TCP服務端,在PC上編寫腳本 創建TCP客戶端,與服務器進行通信。
ESP32 TCP服務端¶
""" ESP32 TCP Server """ import socket from emp_wifi import Wifi port = 10000 #端口號 listenSocket = None #套接字 try: # 注意:線連接到WiFi網絡! # 如果未連接到網絡,以下是連接到網絡的代碼 # Wifi.connect() ip = Wifi.ifconfig()[0][0] #獲取IP地址 listenSocket = socket.socket() #創建套接字 listenSocket.bind((ip, port)) #綁定地址和端口號 listenSocket.listen(1) #監聽套接字, 最多允許一個連接 listenSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #設置套接字 print ('tcp waiting...') while True: print("accepting.....") conn, addr = listenSocket.accept() #接收連接請求,返回收發數據的套接字對象和客戶端地址 print(addr, "connected") while True: data = conn.recv(1024) #接收數據(1024字節大?。?/span> if(len(data) == 0): #判斷客戶端是否斷開連接 print("close socket") conn.close() #關閉套接字 break print(data) ret = conn.send(data) #發送數據 except: if(listenSocket): #判斷套接字是否為空 listenSocket.close() #關閉套接字
PC TCP客戶端¶
# -*- coding: UTF-8 -*- # PC TCP Client import socket # 導入 socket 模塊 s = socket.socket() # 創建 socket 對象 host = '192.168.2.231' # esp32 ip port = 10000 # 設置端口號 s.connect((host, port)) if __name__ == '__main__': while True: msg = raw_input('>>> ') s.send(msg)
收看星球大戰字符動畫¶
blinkenlights.nl網站提供的星球大戰Asciimation服務。它使用端口23上的telnet協議將數據流式傳輸給任何連接的人。
接下來,讓我們建立一個TCP通信,在我們的REPL中觀看星球大戰。
首先要導入套接字模塊:
>>> import socket
然后我們通過域名來獲取服務器的地址
>>> addr_info = socket.getaddrinfo("towel.blinkenlights.nl", 23)
getaddrinfo
函數實際上返回一個地址列表:
[(2, 1, 0, 'towel.blinkenlights.nl', ('94.142.241.111', 23))]
我們只需要獲取服務器的IP地址和端口,對應于該列表第一項的最后一個元組:
>>> addr = addr_info[0][-1]
建立一個socket對象,然后使用上面的IP地址和端口號與服務器進行連接:
>>> s = socket.socket() >>> s.connect(addr)
現在我們已經連接,我們可以獲取并顯示數據了:
>>> while True: ... data = s.recv(500) ... print(str(data, 'utf8'), end='') ...
當這個循環執行時,它應該開始顯示動畫(使用ctrl-C來中斷它)
思考¶
如何使用 Socket通信,來遠程點亮ESP32控制的LED呢?你將制定何種通信協議呢?