Skip to content

Instantly share code, notes, and snippets.

@david-cermak
Created December 17, 2020 18:37
Show Gist options
  • Save david-cermak/19c1b9eba361accc54113b9b98c9cb75 to your computer and use it in GitHub Desktop.
Save david-cermak/19c1b9eba361accc54113b9b98c9cb75 to your computer and use it in GitHub Desktop.
esp32 tcp-server randomly closing sockets
/* BSD Socket API Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"
#include "protocol_examples_common.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#define PORT CONFIG_EXAMPLE_PORT
static const char *TAG = "example";
static int g_fd = 0;
static int g_listener_fd = 0;
static void close_task(void *pvParameters)
{
while (1) {
if (g_listener_fd == 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
ESP_LOGI("close_task", "Closing one of the socket after random delay...");
vTaskDelay(pdMS_TO_TICKS(esp_random() % 10000));
if (esp_random() % 10 > 2) { // make the listener close more likely
ESP_LOGE("close_task", "...closed listener socket");
close(g_listener_fd);
} else {
ESP_LOGE("close_task", "...closed client socket");
close(g_fd);
}
}
vTaskDelete(NULL);
}
static void poll_task(void *pvParameters)
{
while (1) {
if (g_listener_fd == 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
continue;
}
struct pollfd poll_fds[] = {
{
.fd = g_fd,
.events = POLLIN,
},
{
.fd = g_listener_fd,
.events = POLLIN,
}
};
int s = 0;
while (s == 0) {
ESP_LOGI("poll_task", "Before poll");
s = poll(poll_fds, sizeof(poll_fds) / sizeof(poll_fds[0]), 1000);
ESP_LOGI("poll_task", "Poll output %d", s);
if (s < 0) {
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
vTaskDelete(NULL);
}
static void tcp_server_task(void *pvParameters)
{
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
while (1) {
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
int enable = 1;
restart_listener:
g_listener_fd = socket(addr_family, SOCK_STREAM, ip_protocol);
if (g_listener_fd < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelay(pdMS_TO_TICKS(1000));
goto restart_listener;
}
ESP_LOGI(TAG, "Socket created");
if (setsockopt(g_listener_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) < 0) {
/* This will fail if CONFIG_LWIP_SO_REUSE is not enabled. But
* it does not affect the normal working of the HTTP Server */
ESP_LOGW(TAG, "error enabling SO_REUSEADDR (%d)", errno);
}
int err = bind(g_listener_fd, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
vTaskDelay(pdMS_TO_TICKS(1000));
shutdown(g_listener_fd, 0);
close(g_listener_fd);
goto restart_listener;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
err = listen(g_listener_fd, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
vTaskDelay(pdMS_TO_TICKS(1000));
shutdown(g_listener_fd, 0);
close(g_listener_fd);
goto restart_listener;
}
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_in6 source_addr;
uint addr_len = sizeof(source_addr);
struct pollfd poll_fds[] = {
{
.fd = g_listener_fd,
.events = POLLIN,
}
};
int s = 0;
while (s == 0) {
ESP_LOGW(TAG, "Before poll");
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 1000);
ESP_LOGW(TAG, "Poll output %d", s);
if (s < 0) {
ESP_LOGE(TAG, "Error %d (%s)", errno, strerror(errno));
shutdown(g_listener_fd, 0);
close(g_listener_fd);
goto restart_listener;
}
}
g_fd = accept(g_listener_fd, (struct sockaddr *)&source_addr, &addr_len);
if (g_fd < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
shutdown(g_listener_fd, 0);
close(g_listener_fd);
goto restart_listener;
}
ESP_LOGI(TAG, "Socket accepted");
while (1) {
struct pollfd poll_fds[] = {
{
.fd = g_fd,
.events = POLLIN,
},
{
.fd = g_listener_fd,
.events = POLLIN,
}
};
s = 0;
while (s == 0) {
ESP_LOGW(TAG, "Before poll");
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 1000);
ESP_LOGW(TAG, "Poll output %d", s);
if (s < 0) {
ESP_LOGE(TAG, "Error %d (%s)", errno, strerror(errno));
shutdown(g_fd, 0);
shutdown(g_listener_fd, 0);
close(g_listener_fd);
close(g_fd);
goto restart_listener;
}
}
int len = recv(g_fd, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occurred during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Connection closed
else if (len == 0) {
ESP_LOGI(TAG, "Connection closed");
break;
}
// Data received
else {
// Get the sender's ip address as string
if (source_addr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (source_addr.sin6_family == PF_INET6) {
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
int err = send(g_fd, rx_buffer, len, 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
}
}
if (g_fd != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(g_listener_fd, 0);
close(g_listener_fd);
shutdown(g_fd, 0);
close(g_fd);
}
}
vTaskDelete(NULL);
}
void app_main()
{
ESP_ERROR_CHECK(nvs_flash_init());
tcpip_adapter_init();
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
xTaskCreate(tcp_server_task, "tcp_server", 4096, NULL, 5, NULL);
xTaskCreate(poll_task, "poll_task", 4096, NULL, 5, NULL);
xTaskCreate(close_task, "close_task", 4096, NULL, 5, NULL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment