lwIP
TCP 链接关闭后占用的相关资源何时释放?
TCP 链接关闭后占用的相关资源会在 2 MSL,即 120 s, 或者发送的
linger/send_timeout
超时之后释放。
ESP8266 RTOS SDK v3.2 SNTP 校准后误差会逐渐变大,如何解决?
原因是 ESP8266 系统定时器有误差,采用软件定时器,自身存在误差较大。可通过以下几种方法改善:
分支 v3.2 可以通过创建 task 定时重新从服务器同步时间(推荐 300 s)。
分支 release-v3.3 的系统时钟代码有进行重构,目前测试误差较小,并且也可以定时同步服务器时间。
分支 master 继承了 release-v3.3 上的代码重构,除此之外,可通过 menuconfig 配置 SNTP 同步间隔,路径如下:
Component config
>LWIP
>SNTP
>Request interval to update time (ms)
。
ESP8266 是否支持设备端自发自收?
ESP8266 设备端支持自发自收。
需要在 menuconfig 配置选项中把 lwIP 的 LOOPBACK 选项打开:
menuconfig
>Component config
>LWIP
>Enable per-interface loopback
(键 “Y” 使能)。设备端往环回地址 127.0.0.1 发送数据,设备端可以在环回地址读取到自己发送的数据。
TCP/IP 默认配置的数据包长度是多少?
请参考
menuconfig
>Component config
>LWIP
>TCP
>Maximum Segment Size (MSS)
配置的值。
SNTP 协议中使用 UTC 与 GMT 的方法为何获取不到目标时区的时间?
“TZ = UTC-8” 被解释为 POSIX 时区。在 POSIX 时区格式中,这 3 个字母是时区的缩写(任意),数字是时区落后于 UTC 的小时数。
“UTC-8” 表示时区,缩写为 “UTC”,比实际 UTC 晚 -8 小时,即 UTC + 8 小时。故 UTC+8 是比 UTC 落后 8 小时,就出现了 UTC+8 比正确的北京时间相差 16 小时的情况。
ESP32 是否有特殊的固件或者 SDK,可以不使用芯片内部的 TCP/IP 协议仅提供 AP/STA (TCP/IP bypass),以给开发者更多的权限?
ESP-Dongle 的软件方案符合您的上述需求,请联系 商务 签署 NDA 后获取相关方案。
ESP32 & ESP8266 做 TCP server 时端口释放后如何立即被再次使用?
在 ESP32 和 ESP8266 上,TCP 端口关闭后并不会立即释放,而是在一定时间内处于 TIME_WAIT 状态,此时绑定与之前相同端口相同源地址的套接字会失败,这是为了确保客户端接收到服务器发送的 FIN 信号并成功关闭连接。在这个状态下,端口无法立即重新使用。需要借助套接字选项
SO_REUSEADDR
,它的作用是允许设备绑定处于TIME-WAIT
状态,端口和源地址与之前相同的 TCP 套接字。故 TCP server 程序可以在调用 bind() 之前设置
SO_REUSEADDR
套接字选项后来绑定同样的端口。也可以使用 setsockopt() 函数来设置 SO_REUSEADDR 选项。以下是一个示例代码:
int reuse = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { ESP_LOGE(TAG, "setsockopt(SO_REUSEADDR) failed"); return ESP_FAIL; }在以上代码中,socket 是一个已经创建的套接字,reuse 是一个 int 类型的变量,其值为 1,表示开启 SO_REUSEADDR 选项。如果 setsockopt() 函数返回负值,表示设置失败。 设置 SO_REUSEADDR 选项可以让端口在关闭后立即被再次使用,但也需要注意潜在的风险。如果在端口仍处于 TIME_WAIT 状态时,另一个连接使用该端口,可能会导致数据包错乱,因此需要根据实际情况权衡利弊。
使用 ESP32 模组下载 tcp_client 例程,通过 Wi-Fi 连接路由器,在电脑端进行 Ping 测试,偶尔出现高延时,是什么原因?
Wi-Fi 默认开启 Power Save 模式,关闭 Power Save 可降低由于 Power Save 引起的 Ping 高延时,在 esp_wifi_start() 之后调用 esp_wifi_set_ps(WIFI_PS_NONE) 来关闭 Power Save 模式。
使用 ESP-IDF 如何设置静态 IP?
可以参考 static_ip 示例。
ESP32 有没有 LTE 连接示例?
可以参考 ESP-IDF v4.2 及以上版本里的 examples/protocols/pppos_client 示例。
对于 ESP-IDF v5.0 及以上版本,请参考 esp-protocols 仓库下 示例。
ESP32 TCP 反复关闭并重建 socket 时会出现内存泄漏的情况 (ESP-IDF v3.3),原因是什么?
IDF v3.3 版本,每次创建 socket 时,如果内部该 socket 数组没有分配过锁,就会给该 socket 分配锁,并且该锁在 socket 释放后并不会回收,下次分配该 socket 数组时就使用之前分配的。所以每次分配新的 socket 数组后释放,就会多一个锁的内存消耗。当每个 socket 数组都分配一遍后,就不会存在内存泄漏。
ESP32 额外开启 TCP server 后对 TCP client 的最大连接数是否有限制?
有限制,ESP32 同时存在的 socket fd 数量受限于
LWIP_MAX_SOCKETS
,默认为 10。
使用 ESP32,lwIP 的 MTU 默认是多大?
lwIP 的 MTU 默认是 1500(固定值),不建议自行修改。
ESP32 如何增大 DNS 请求时间?
可以手动修改位于 esp-idf/components/lwip/lwip/src/include/lwip/opt.h 里的
#define DNS_MAX_RETRIES 4
,例如将#define DNS_MAX_RETRIES
的值改成 10,这样 DNS 在一个服务器上会尝试 10 次域名请求,每次请求的超时时间(s)是 1,1,2,3,4,5,6,7,8,9,总时间是 46 s。
连续多次创建并关闭 TCP SOCKET 后出现报错 “Unable to create TCP socket: errno 23”,怎么解决?
:CHIP: ESP8266 | ESP32 | ESP32-S2 | ESP32-C3 | ESP32-S3 :
原因:”errno 23” 代表的是 open many open files in system,由于关闭 socket 需要 2 MSL 的时间,所以调用 close 接口并不会立即关闭,导致 socket 持续累加,超过了 socket 最大支持连接数(menuconfig 中默认是 10 个,最大支持 16 个)报错。
解决措施:通过 setsockopt 接口设置 SO_LINGER 来调整 TCP 关闭时间,代码实现参考:
linger link ;
link.on_off = 1 ;
link.linger = 0 ;
setsockopt(m_sockConnect, SOL_SOCKET, SO_LINGER, (const char*)&link, sizeof(linger));
ESP8266 收到 “tcp out of order” 的报文会怎么处理?
如果使能
CONFIG_LWIP_TCP_QUEUE_OOSEQ(Component config -> LWIP -> TCP -> Queue incoming out-of-order segments)
,会存储 “out of order” 的报文,代价是消耗内存。如果该配置是未使能,收到 “out of order” 的报文,会丢弃数据并让对端重传。比如现在有 1、2、3、4 四包数据,ESP8266 先收到 1 然后收到 4。该配置使能时,ESP8266 会把 4 这个数据存下来,等收到 2、3 后,把这四包数据上报应用层;该配置未使能时,ESP8266 会直接丢弃 4,并让对端发送包 2,对端就会从 2 开始发送,即该情况下会增加重传。
ES32 支持 PPP 功能吗?
支持,请参考 esp_modem 示例。
ESP32 使用套接字中的 read
和 recv
API 读取 4 KB 数据时,发现并不是每次都能读到 4 KB 的数据。这种情况如何解释?
read
和recv
API 都是用来读底层缓冲区中的数据,比如底层缓冲区中有 100 字节数据,read
和recv
传入的len
大小只有 50 字节,那么 API 读到 50 字节的数据时就会返回;如果传入的len
超过底层缓冲区中接收到的数据的长度,比如 200 字节,此时 API 读到 100 字节就会返回,并不会等到接收到 200 字节才返回。所以传入 4 KB 的长度的数据并不一定会返回 4KB 长度的数据,只会返回读取时底层缓冲区中有的数据。如果需要每次都读取到 4 KB 的数据,建议在套接字层之上使用应用代码设计对应的逻辑,让应用代码循环读取数据直到满足 4 KB 的大小。
ESP-IDF 里目前使用的 lwIP 版本是什么?
目前使用的 lwIP 版本是 2.1.3。
在 DHCP 模式下,ESP32 申请到 IP 后,如果租期到期,会续约此 IP 还是重新申请 IP?
DHCP 模式下有两个租期,T1(租约的 1/2 时间)和 T2(租约的 7/8 时间),通常这两个租期期满后会续租同一 IP,只有当上述两个租期时间点都续租失败,才会重新申请 IP。
ESP-IDF 里使用 setsockopt
的 SO_SNDBUF
选项获取或者设置发送缓冲区大小会报错,为什么?
lwIP 默认不支持
SO_SNDBUF
选项,如果需要配置发送缓冲区大小可以在 menuconfig ->Component config
->LWIP
->TCP
->Default send buffer size
设置。如果需要获取或者设置接收缓冲区大小,此时需要在 menuconfig 里使能CONFIG_LWIP_SO_RCVBUF
选项后才支持使用setsockopt
的SO_RCVBUF
选项获取或者设置接收缓冲区大小。
使用 ESP-IDF 测试发现 TCP & UDP 的网络数据延时较大,请问 TCP & UDP 协议的缓冲数据机制是什么?
对于 TCP,套接字选项里有
TCP_NODELAY
选项,可以使能该选项来禁用默认使能的 Nagle 算法,这样就不会出现本地缓存一定数据后再一起发送的情况。对于 UDP,UDP 的数据交互采取直接发送的形式,如果有延迟,也是 Wi-Fi 网络环境的延迟,和 UDP 本身无关。
如果是网络环境较差导致 TCP 重传,重传的间隔设置过大会导致延迟高,可以尝试缩短 RTO 的值(通过修改 menuconfig 里的
component config
->lwip
->tcp
->Default TCP rto time
和TCP timer interval
选项)。
ESP32 做双网卡(比如 ETH+STA)时,默认路由如何选择?
以下总结了双网卡时默认路由如何选择,以 ETH 和 STA 为例:
假设 ETH 和 STA 在同一个局域网:
当设备访问局域网地址时,数据走最后 up 的 netif。
当设备访问非局域网内地址时,数据走
route_prio
值大的 netif。假设 ETH 和 STA 不在一个局域网,ETH 属于 192.168.3.x 网段,STA 属于 192.168.2.x 网段:
当设备访问 192.168.3.5 时,就会走 ETH netif。
当设备访问 192.168.2.5 时,就会走 STA netif。
当设备访问 10.10.10.10 时,就会走默认路由(
route_prio
值大的 netif)。netif 起来后,会根据route_prio
值大小设置默认路由,默认路由往往是route_prio
值大的 netif。当设备访问的地址不在路由表里时,数据就会走默认路由。
ESP-IDF 里 TCP 如何开启 keepalive?
可以参考 esp_tls.c 里的使能 TCP keepalive 相关代码。
ESP-IDF 里可以在多线程里操作同一个套接字吗?
可以,在 ESP-IDF 中,多线程可以共享同一个套接字进行通信。每个线程都可以使用同一个套接字来发送和接收数据,但需要确保在访问套接字时进行线程同步,避免竞争条件和死锁等问题。一般可以使用互斥锁(mutex)来控制套接字的访问,确保每个线程访问套接字时都是互斥的,避免出现同时访问套接字导致数据混乱的情况。多线程操作同一个套接字有风险,不建议该做法。
ESP DHCP 服务器模式下,ESP 设备分配到其他设备 IP 的时间是多少?
默认为 120 s,具体见
DHCPS_LEASE_TIME_DEF
参数,不建议修改为太小的值。
ESP-IDF DHCP 里三个租约相关时间是指什么?具体对应代码里的什么参数?
DHCP 有租约时间 (Address Lease Time)、租约续期时 (Lease Renewal Time) 和租约重新设定的时间 (Lease Rebinding Time),分别对应 lwIP 代码
offered_t0_lease
、offered_t1_renew
和offered_t2_rebind
。
ESP-IDF lwIP 里每次发送数据的最大长度是多少?
如果使用套接字接口
send
,支持最大长度有SSIZE_MAX
参数决定。如果使用tcp_write
函数,最大发送的长度受限于snd_buf
(发送缓存区长度)。send
接口是 lwIP 基于顺序 API 封装的套接字接口,是比tcp_write
还要上层的接口,更适合于用户层开发调用。这两个 API 调用资源占用几乎没有差别。
使用 ESP-IDF 出现 lwIP 层相关问题需要更多的调试日志时,如何使能对应的调试日志打印(如 lwIP 下的 DHCP 和 IP 等)?
可以在 menuconfig 里使能 lwIP 相关调试日志选项,具体的选项为:menuconfig ->
Component config
->LWIP
->Enable LWIP Debug
。其中有子选项Enable IP debug messages
、Enable DHCP debug messages
等,可以按实际需要进行勾选来开启对应的调试日志。如在上述 menuconfig 里没有找到想要的调试日志模块,如 UDP 模块,请首先检查
esp-idf/components/lwip/port/esp32/include/lwipopts.h
中是否有#define UDP_DEBUG
,如果有,可以手动将#define UDP_DEBUG LWIP_DBG_OFF
修改为#define UDP_DEBUG LWIP_DBG_ON
。如果没有,可以参照 esp-idf/components/lwip/lwip/src/include/lwip/opt.h 文件下的#define UDP_DEBUG LWIP_DBG_OFF
,在esp-idf/components/lwip/port/esp32/include/lwipopts.h
里加一行#define UDP_DEBUG LWIP_DBG_ON
。
ESP-IDF 中套接字阻塞和非阻塞的区别是什么?
对于读而言,阻塞和非阻塞的区别在于底层没有数据到达时读接口是否立刻返回。阻塞的读会一直等到读取到数据或者异常,非阻塞的读会立刻返回,无论有无数据。
对于写而言,阻塞和非阻塞的区别在于底层缓冲区满了后写接口是否立刻返回。阻塞的写,如果底层不可写(底层缓冲区满了或者对端没有 ack 之前发送的数据),这时候的写操作会一直阻塞,直到可写或者异常才会退出;非阻塞的写是可以写多少就写多少,无需等待底层是否可写,返回写入的长度。
非阻塞接口调用后不会阻塞当前进程继续执行,阻塞接口调用后会阻塞当前进程执行。
ESP32 是否支持在连上路由后使用上一次成功连接路由器时的 IP 进行通信,如果失败再重新开始认证流程,通过 DHCP 来获取新的 IP?
支持,可以在 menuconfig 里使能
Component config
>LWIP ->DHCP: Restore last IP obtained from DHCP server
选项。需要注意的是,此时不能用静态 IP 来代替,因为静态 IP 设置没有冲突检测,可能会导致 IP 冲突。
使用 socket 编程时,如何实现 connect 超时?
将 socket 设置为非阻塞模式,connect() 函数也会是非阻塞,之后通过 select() 函数设置超时时间来判断 socket 是否连接成功,详细操作可参考 “socket 连接超时设置”。
ESP32 使用 SNTP 同步实时时间时发现存在随机延时,进一步分析后发现是 IDF lwip 组件里的 SNTP_STARTUP_DELAY
默认为 1 导致的,是否有办法在不修改 IDF 组件的情况下去掉随机延时?
目前没有办法在不修改 IDF 组件的情况下去掉随机延时,需要手动在 lwip 组件里的 lwipopts.h 文件里加上
#define SNTP_STARTUP_DELAY 0
这一行代码。这个修改可以减少 SNTP 发送请求的时间,进而减少 ESP 设备上电后到连云成功的整体时间。默认使能此随机延时选项的原因是:这是由 SNTP RFC 协议规定的,防止 SNTP 服务器负载太高,有个随机的 delay 值可以减少设备同时访问的数量。
IPv4 和 IPv6 是否支持设置静态 IP?
如果是本地静态 IP,IPv4 支持设置,但 IPv6 的本地 IP 是按照协议规则自动生成的,无需手动设置。
如果是全局静态 IP,IPv6 和 IPv4 都支持设置。
安卓手机连上 ESP SoftAP 后,手机会提示 “不可上网是否继续使用”,若选择 “不使用”,手机仍能通过流量访问外部网页,但同样的操作,苹果手机无法通过流量访问外部网页。这是什么原因?如何解决?
原因:ESP DHCP Server 回复的 DHCP ACK 里包含 option3 (router) 字段,因此手机解析该 option 后,会设置默认路由为 192.168.4.1,导致苹果手机后续通过 ESP Wi-Fi 而不是流量访问外部网页。
解决方法:将 dhcpserver.c 的这部分代码段 注释即可。
TCP 或者 UDP 发送失败,提示错误码 12(ENOMEM),如何解决?
12 表示内存不足,建议先打印剩余内部内存,如果内部内存比较充足,那么则是 Wi-Fi 的 TX buffer 满了, 建议应用层发送慢一点或者在 sdkconfig 中增大 Wi-Fi TX buffer。