此篇博客用来记录 ESP32 TCP 相关应用层操作。
1 socket 关闭后立即复用地址端口
先通过 idf.py menuconfig->component config->lwip->Enable SO_REUSEADDR option 选项使能 SO_REUSEADDR socket option(大部分 menuconfig 默认使能了此选项),每次创建 socket 后添加以下代码:
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
此时在此 socket 关闭后可立即使用同样的地址端口正常通信。
2 使能 TCP keepalive 并且设置 keepalive 用于检查僵尸客户端
使能 TCP keepalive 并且设置 keepalive 的参考代码可以参考 这里,如下:
if (cfg->keep_alive_cfg && cfg->keep_alive_cfg->keep_alive_enable) {
int keep_alive_enable = 1;
int keep_alive_idle = cfg->keep_alive_cfg->keep_alive_idle;
int keep_alive_interval = cfg->keep_alive_cfg->keep_alive_interval;
int keep_alive_count = cfg->keep_alive_cfg->keep_alive_count;
ESP_LOGD(TAG, "Enable TCP keep alive. idle: %d, interval: %d, count: %d", keep_alive_idle, keep_alive_interval, keep_alive_count);
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive_enable, sizeof(keep_alive_enable)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt SO_KEEPALIVE");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_alive_idle, sizeof(keep_alive_idle)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPIDLE");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &keep_alive_interval, sizeof(keep_alive_interval)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPINTVL");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_alive_count, sizeof(keep_alive_count)) != 0) {
ESP_LOGE(TAG, "Fail to setsockopt TCP_KEEPCNT");
return ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED;
}
}
3 使用 SO_LINGER socket option 强制发送 RST 断开连接
如果关闭 socket,对端不回 FIN 导致 socket 处于 TIME_WAIT 状态, 在 close socket 的地方加 SO_LINGER socket option 强制发送 RST 断开连接。此时先通过 idf.py menuconfig->component config->lwip->Enable SO_LINGER processing 选项使能 SO_LINGER socket option,然后在关闭 socket 前添加以下代码:
void onClose(httpd_handle_t hd, int sockfd) {
esp_tls_t *tls = httpd_sess_get_transport_ctx(hd, sockfd);
esp_tls_conn_delete(tls);
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 0;
if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger) == 0) {
ESP_LOGI(TAG, "Set SO_LINGER success");
close(sockfd);
}
ESP_LOGI(TAG, "Closing Socket %d, Heap:%d", sockfd, esp_get_free_heap_size());
}
注:这部分的参考资料点击 这里。
|