文章目录
- ❤️UDP通信,客户端与服务器❤️
- 📢TCP与UDP优缺点
- ☀️UDPClient —— 客户端请求
- 1.1 创建STA模式
- 1.2 激活station模式
- 1.3 连接到您的WiFi网络
- 1.4 检查连接是否建立
- 1.5 创建socket对象
- 1.6 构建ServerIP+ServerPort
- 1.7 发起请求
- 1.8 响应请求
- 1.9 关闭socket
- ✨UDPClient 示例
- 本地局域网PC机构建UDPServer,ESP32访问它
- ☀️UDPServer —— 服务端响应
- 2.1 创建STA模式
- 2.2 激活station模式
- 2.3 连接到您的WiFi网络
- 2.4 检查连接是否建立
- 2.5 创建socket对象
- 2.6 构建ServerIP+ServerPort
- 2.7 绑定地址
- 2.8 响应请求
- 2.9 关闭socket
- ✨UDPServer 示例
- 本地局域网PC机构建UDPClient,访问ESP32 UDPServer
❤️UDP通信,客户端与服务器❤️
📢TCP与UDP优缺点
- TCP是
面向连接
,也就是发送数据之前是需要建立连接;UDP是面向无连接
的,即发送数据之前不需要建立连接。
- 首先 UDP 是不需要和 TCP一样在发送数据前进行
三次握手
建立连接的,想发数据就可以开始发送了。并且也只是数据报文
的搬运工,不会对数据报文进行任何拆分
和拼接
操作。(也就是每个数据报之间是独立的,没有任何关联先后顺序,因此应用层数据必须选择合适大小的报文数据。)- 在网络数据传输模型中,发送端应用层将数据传递给传输层的 UDP,UDP 协议只会给数据增加一个 UDP 头标识下是 UDP 协议,然后就传递给网络IP层了
在接收端,网络IP层将数据传递给传输层,UDP 只去除 IP 报文头就传递给应用层,不会任何拼接操作
- TCP提供
可靠
的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力做到可靠,即不保证绝对可靠
。
- 首先不可靠性体现在
无连接
上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。
并且收到什么数据就传递什么数据,并且也不会备份
数据,发送数据也不会关心对方是否已经正确接收到数据了。- UDP 没有
拥塞控制
,一直会以恒定的速度发送数据。即使网络条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网络条件不好的情况下可能会导致丢包
,但是优点也很明显,在某些实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP,因为丢弃了一些数据无关紧要。
- UDP具有较好的实时性,工作
效率
比TCP高,适用于对高速传输和实时性有较高的通信或广播
通信。
- UDP头部开销小,传输数据报文时是很高效的
- 每一条TCP连接只能是
点到点
的;UDP支持一对一,一对多,多对一和多对多的交互通信。 - TCP对系统资源要求较多,UDP对系统资源要求较少。
UDP 是 User Datagram Protocol
的简称,是一种无连接、不可靠的协议,每一个数据报
都是一个独立的信息,它在网络上以任何可能的路径传到目的地,但不保证是否真的传到目的地、是否过程中真的保证了数据的完整性!
UDP就好像无手机时代,你要去探望亲戚,但是你不知道亲戚有没有在家(也就是说可能会丢包);
TCP就好像有手机时代,你要去探望亲戚,你会打电话过去提前沟通好,你会确保亲戚在家里才会买东西过去探望(数据不会丢包);
- MicroPython 提供丰富的
网络功能
,可以加快物联网应用的开发速度。了解网络功能之后,就可以将产品轻松的接入网络,实现更多物联网功能。- 在使用 MicroPython 进行网络编程首先需要了解的就是 usocket 模块,模块提供对
BSD套接字接口
的访问。
而在TCP/UDP基础上我们又会TCPClient
、TCPServer
、UDPClient
、UDPServer
。TCP相关已经讲解,本篇重点讲解UDP相关。
目前基于UDP的应用层协议应用比较广泛就是
CoAP
。
而在usocket
模块中,我们需要了解一些基本的常量(网络基础)含义:
- IP地址类型
socket.AF_INET =2 — TCP/IP –
IPv4
socket.AF_INET6 =10 — TCP/IP –IPv6
- 套接字类型
socket.
SOCK_STREAM
=1 — TCP流(重点)
socket.SOCK_DGRAM
=2 — UDP数据报(重点)
socket.SOCK_RAW =3 — 原始套接字
socket.SO_REUSEADDR =4 — socket可重用
- IP协议号(在大多数情况下不需要指定这个,MicroPython也不推荐,作为了解即可)
socket.IPPROTO_TCP =16
socket.IPPROTO_UDP =17
- 原因:因为 SOCK_STREAM 套接字类型会自动选择 IPPROTO_TCP 和 SOCK_DGRAM - IPPROTO_UDP。 因此,这些常量的唯一实际用途是作为 setsockopt() 的参数。
☀️UDPClient —— 客户端请求
这里博主特意用两种颜色区分STA和Client过程。
1.1 创建STA模式
import network
sta_if = network.WLAN(network.STA_IF)
注意:
- 第一次按照MicroPython固件的时候,ESP32配置为热点模式,因此AP_IF接口有效,STA_IF接口无效。
1.2 激活station模式
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
1.3 连接到您的WiFi网络
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect('<your ESSID>', '<your password>')
1.4 检查连接是否建立
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.isconnected()
1.5 创建socket对象
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
- 使用给定的地址群、类型和协议号创建一个新的socket对象
1.6 构建ServerIP+ServerPort
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
- 将参数翻译为一个5
元组
序列,该序列包含创建一个与设备连接的socket所需的全部必要参数。 - 该5元组列表有以下结构:
(family, sockettype, proto, canonname,
sockaddr
)
简称ftpca
family
: 表示socket使用的协议簇
。常用的协议簇包括AF_UNIX(本机通信)/AF_INET(TCP/IP协议簇中的IPv4协议)/AF_INET6(TCP/IP协议簇中的IPv4协议)。在python的socket包中,用1表示AF_UNIX,2表示AF_INET,10表示AF_INET6。sockettype
:表示socket的类型。常见的socket类型包括SOCK_STREAM(TCP流)/SOCK_DGRAM(UDP数据报)/SOCK_RAW(原始套接字)。其中,SOCK_STREAM
=1,SOCK_DGRAM
=2,SOCK_RAW=3proto
:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP
(=6)和IPPTOTO_UDP
(=17),它们分别对应TCP传输协议、UDP传输协议。canonname
:主机名字sockaddr
:IP地址+端口
注意:
- socket.getaddrinfo 返回的是一个元组数组(这里可以解释
[0][-1]
的由来,获取到IP地址和端口元组)。类似于:
[(2, 1, 6, '', ('220.181.111.86', 80)),
(2, 2, 17, '', ('220.181.111.86', 80)),
(2, 1, 6, '', ('123.125.114.144', 80)),
(2, 2, 17, '', ('123.125.114.144', 80)),
(2, 1, 6, '', ('220.181.111.85', 80)),
(2, 2, 17, '', ('220.181.111.85', 80))]
- 这个ftpca的 前三位可以用来构造一个socket。
2 是 AF_INET
1 是 SOCK_STREAM
6 是 IPPROTOTCP
1.7 发起请求
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 发起请求
print("esp32 micropython udp")
s.sendto("esp32 micropython udp",addr)
1.8 响应请求
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 发起请求
print("esp32 micropython udp")
s.sendto("esp32 micropython udp",addr)
# 响应请求
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
- 从socket上接收数据。返回值是一个表示接收到的数据的字节对象。要接收到的数据最大数量由缓冲区大小指定。
1.9 关闭socket
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 发起请求
print("esp32 micropython udp")
s.sendto("esp32 micropython udp",addr)
# 响应请求
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
# 关闭socket
s.close()
- 标记关闭的socket。一旦发生这种情况,所有将对socket对象进行的操作都将失败。远程端将无法再接收到任何数据(队列数据刷新后)。
- Socket在垃圾回收时自动关闭,但是建议您明确地关闭它们
✨UDPClient 示例
本地局域网PC机构建UDPServer,ESP32访问它
步骤:
- 网络调试助手打开
UDPServer
,获取本地主机IP
地址和本地主机端口号port
- esp32创建
UDPClient
,连接该IP和port,发送 “esp32 micropython udp”过去
- 新建
network_udp_client.py
# 导入usocket模块
import usocket
# 导入网络模块
import network
# 定义一个连接函数
def do_connect():
# 创建STA模式
sta_if = network.WLAN(network.STA_IF)
# 返回网络工作状态
print('network status1:', sta_if.status())
# 检查连接是否建立
if not sta_if.isconnected():
print('connecting to network...')
# 激活station模式
sta_if.active(True)
# 连接到您的WiFi网络
sta_if.connect('TP-LINK_5344', 'xxxxxx')
# 返回网络工作状态
print('network status2:', sta_if.status())
# 检查连接是否建立
while not sta_if.isconnected():
pass
# 返回网络工作状态
print('network status3:', sta_if.status())
# 开机自动连接
do_connect()
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort,这里得填写自己的网络调试助手
addr = usocket.getaddrinfo('192.168.1.105', 32666)[0][-1]
print(addr)
# 发起请求
print("esp32 micropython udp")
s.sendto("esp32 micropython udp",addr)
# 响应请求
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
# 关闭socket
s.close()
- 结果:
>>> %Run -c $EDITOR_CONTENT
network status1: 1010
network status3: 1010
('192.168.1.105', 32666)
esp32 micropython udp
☀️UDPServer —— 服务端响应
这里博主特意用两种颜色区分STA和Server过程。
2.1 创建STA模式
import network
sta_if = network.WLAN(network.STA_IF)
注意:
- 第一次按照MicroPython固件的时候,ESP32配置为热点模式,因此AP_IF接口有效,STA_IF接口无效。
2.2 激活station模式
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
2.3 连接到您的WiFi网络
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect('<your ESSID>', '<your password>')
2.4 检查连接是否建立
import network
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.isconnected()
2.5 创建socket对象
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
- 使用给定的地址群、类型和协议号创建一个新的socket对象
2.6 构建ServerIP+ServerPort
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
- 将参数翻译为一个5
元组
序列,该序列包含创建一个与设备连接的socket所需的全部必要参数。 - 该5元组列表有以下结构:
(family, sockettype, proto, canonname,
sockaddr
)
简称ftpca
family
: 表示socket使用的协议簇
。常用的协议簇包括AF_UNIX(本机通信)/AF_INET(TCP/IP协议簇中的IPv4协议)/AF_INET6(TCP/IP协议簇中的IPv4协议)。在python的socket包中,用1表示AF_UNIX,2表示AF_INET,10表示AF_INET6。sockettype
:表示socket的类型。常见的socket类型包括SOCK_STREAM(TCP流)/SOCK_DGRAM(UDP数据报)/SOCK_RAW(原始套接字)。其中,SOCK_STREAM
=1,SOCK_DGRAM
=2,SOCK_RAW=3proto
:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP
(=6)和IPPTOTO_UDP
(=17),它们分别对应TCP传输协议、UDP传输协议。canonname
:主机名字sockaddr
:IP地址+端口
注意:
- socket.getaddrinfo 返回的是一个元组数组(这里可以解释
[0][-1]
的由来,获取到IP地址和端口元组)。类似于:
[(2, 1, 6, '', ('220.181.111.86', 80)),
(2, 2, 17, '', ('220.181.111.86', 80)),
(2, 1, 6, '', ('123.125.114.144', 80)),
(2, 2, 17, '', ('123.125.114.144', 80)),
(2, 1, 6, '', ('220.181.111.85', 80)),
(2, 2, 17, '', ('220.181.111.85', 80))]
- 这个ftpca的 前三位可以用来构造一个socket。
2 是 AF_INET
1 是 SOCK_STREAM
6 是 IPPROTOTCP
2.7 绑定地址
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 绑定地址
s.bind(addr)
- 将套接字绑定到地址,套接字不能是已经绑定的。
2.8 响应请求
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 绑定地址
s.bind(addr
# 响应请求
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
- 从socket上接收数据。返回值是一个表示接收到的数据的字节对象。要接收到的数据最大数量由缓冲区大小指定。
2.9 关闭socket
# 导入usocket模块
import usocket
# 创建 udp socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort
addr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# 发起请求
print("esp32 micropython udp")
s.sendto("esp32 micropython udp",addr)
# 响应请求
while True:
data = s.recv(100)
if data:
print(str(data, 'utf8'), end='')
else:
break
# 关闭socket
s.close()
- 标记关闭的socket。一旦发生这种情况,所有将对socket对象进行的操作都将失败。远程端将无法再接收到任何数据(队列数据刷新后)。
- Socket在垃圾回收时自动关闭,但是建议您明确地关闭它们
✨UDPServer 示例
本地局域网PC机构建UDPClient,访问ESP32 UDPServer
步骤:
- 网络调试助手打开
UDPClient
,获取本地主机IP
地址和本地主机端口号port
- esp32创建
UDPServer
,处理发过来的数据
- 新建
network_udp_server.py
# 导入usocket模块
import usocket
# 导入网络模块
import network
# 定义一个连接函数
def do_connect():
# 创建STA模式
sta_if = network.WLAN(network.STA_IF)
# 返回网络工作状态
print('network status1:', sta_if.status())
# 检查连接是否建立
if not sta_if.isconnected():
print('connecting to network...')
# 激活station模式
sta_if.active(True)
# 连接到您的WiFi网络
sta_if.connect('TP-LINK_5344', 'xxxxxxxx')
# 返回网络工作状态
print('network status2:', sta_if.status())
# 检查连接是否建立
while not sta_if.isconnected():
pass
# 返回网络工作状态
print('network :', sta_if.ifconfig())
# 开机自动连接
do_connect()
# 创建 UDP socket
s = usocket.socket(usocket.AF_INET,usocket.SOCK_DGRAM)
# 构建ServerIP+ServerPort,这里得填写自己的网络调试助手
addr = usocket.getaddrinfo('192.168.1.102', 32666)[0][-1]
print(addr)
# 绑定地址
s.bind(addr)
# 响应请求
while True:
data,clientaddr = s.recvfrom(1024)
print("data:",data)
print("from:",clientaddr)
if data:
print(str(data, 'utf8'), end='')
else:
break
# 关闭socket
s.close()
结果:
>>> %Run -c $EDITOR_CONTENT
network status1: 1010
network : ('192.168.1.102', '255.255.255.0', '192.168.1.1', '192.168.1.1')
('192.168.1.102', 32666)
data: b'from Packet Sender'
from: ('192.168.1.105', 64657)
from Packet Senderdata: b'from Packet Sender'
from: ('192.168.1.105', 64657)
from Packet Sender