select() unblocked but recv() timed out

josephhui
Posts: 4
Joined: Thu Apr 02, 2020 10:00 am

select() unblocked but recv() timed out

Postby josephhui » Thu Apr 02, 2020 10:26 am

Hi,

I am implementing a simple web server using LWIP socket that only handles 1 request at a time with a simple loop.

Within the loop, I am using select() to block the process until there is data coming in. I have no problem in receiving HTTP request and responding to it. The issue is that after a request is handled, the next select() will immediately unblock even if there is no new request coming in. This results in recv() being blocked until recv() times out. Is this normal?

For the time being, I am ignoring this timeout error. But, I am wondering if I may have done something wrong.

Joseph

ESP_Sprite
Posts: 3469
Joined: Thu Nov 26, 2015 4:08 am

Re: select() unblocked but recv() timed out

Postby ESP_Sprite » Thu Apr 02, 2020 1:24 pm

Can you show the code you're using?

josephhui
Posts: 4
Joined: Thu Apr 02, 2020 10:00 am

Re: select() unblocked but recv() timed out

Postby josephhui » Thu Apr 02, 2020 3:08 pm

Actually the errno return is 11 (EAGAIN). I guess it means there is no data available. I will try to post the code a bit later.

josephhui
Posts: 4
Joined: Thu Apr 02, 2020 10:00 am

Re: select() unblocked but recv() timed out

Postby josephhui » Thu Apr 02, 2020 3:20 pm

Below is the code. Use xTaskCreate() to start the http server task.

Code: Select all

#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "nvs_flash.h"
#include <esp_http_server.h>
#include "lwip/api.h"
#include "lwip/sockets.h"
#include "lwip/err.h"
#include "lwip/netdb.h"
#include "util/ctrl_sock.h"


static const char *TAG = "httdTask";

#define CTRL_PORT  			32768
#define CTRL_MSG_SHUTDOWN	4723
int ctrl_fd = -1;


// ====================================================================================================

const static char http_hdr_http11[] = "HTTP/1.1 ";
const static char http_hdr_200[] = "200 OK\r\n";
const static char http_hdr_302[] = "302 Found\r\n";
const static char http_hdr_404[] = "404 Not Found\r\n";
const static char http_hdr_content_type[]= "Content-type: text/html\r\n\r\n";
const static char http_hdr_location[] = "Location: ";


//======================================================================
void socket_write(const int sock, const char *str, const int len) {
    int to_write = len;
    while (to_write > 0) {
        int written = send(sock, str + (len - to_write), to_write, 0);
        if (written < 0) {
            ESP_LOGE(TAG, "HTTP Server: Error occurred during sending: errno %d", errno);
        }
        to_write -= written;
    }
}



// ====================================================================================================
static void http_server_sock_serve(const int sock) {
	int len;
	char rx_buffer[512];	// this should hopefully provide enough buffer to flush to input stream of data.
						// if not, this could potentially cause problem.

	ESP_LOGI(TAG, "HTTP Server: Calling recv()");
	len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);

	if (len < 0) {
		if (errno == EAGAIN)
			ESP_LOGI(TAG, "HTTP Server: recv() timed out with err EAGAIN.");
		else
			ESP_LOGE(TAG, "HTTP Server: Error occurred during receiving: errno %d", errno);
	} else if (len == 0) {
		ESP_LOGW(TAG, "HTTP Server: Connection closed");
	} else {
			rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string

			ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

			// extract the first line, with the request
			char *first_line = strtok(rx_buffer, "\n");

			if(first_line) {
			// default page
			if(strstr(first_line, "GET / ")) {		// redirect
				ESP_LOGI(TAG, "HTTP Redirect received");
				socket_write(sock, http_hdr_http11, strlen(http_hdr_http11));
				socket_write(sock, http_hdr_302, strlen(http_hdr_302));
				socket_write(sock, http_hdr_location, strlen(http_hdr_location));
				socket_write(sock, "/index.html", 11);

				ESP_LOGI(TAG, "Replied with URL redirect.");
			}
			else if(strstr(first_line, "GET /index.html ")) {  // index page
				const char *testoutput="<html><body>Test Index.html</body></html>";
				ESP_LOGI(TAG, "HTTP /index.html received");
				socket_write(sock, http_hdr_http11, strlen(http_hdr_http11));
				socket_write(sock, http_hdr_200, strlen(http_hdr_200));
				socket_write(sock, http_hdr_content_type, strlen(http_hdr_content_type));
				socket_write(sock, testoutput, strlen(testoutput));

				ESP_LOGI(TAG, "Replied with content of index.html.");
			}
			else {
				ESP_LOGI(TAG, "HTTP Unknown Request received");
				socket_write(sock, http_hdr_http11, strlen(http_hdr_http11));
				socket_write(sock, http_hdr_404, strlen(http_hdr_404));
				ESP_LOGI(TAG,"Unkown request: %s\n", first_line);
			}
		}
		else {
			ESP_LOGI(TAG, "HTTP Unknown Request received");
			socket_write(sock, http_hdr_http11, strlen(http_hdr_http11));
			socket_write(sock, http_hdr_404, strlen(http_hdr_404));
			ESP_LOGI(TAG,"Unkown request\n");
		}
	}
}


// ====================================================================================================
bool shutting_down = false;
void http_server(void *pvParameters) {

    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = IPPROTO_IP;
    fd_set read_set;
    int max_sock;
    int rc;
    struct timeval      timeout;
    struct sockaddr_in6 serv_addr;
    struct sockaddr_in *serv_addr_ip4 = (struct sockaddr_in *)&serv_addr;
    int listen_sock=-1;
    int enable=1;
    int err=0;

    shutting_down = false;

    ESP_LOGI(TAG, "Starting http server.");

    // Create the control sock which is for waking up select() during server shutdown
    ctrl_fd = cs_create_ctrl_sock(CTRL_PORT);
    if (ctrl_fd < 0) {
        ESP_LOGE(TAG, "error in creating ctrl socket (%d)", errno);
		goto CLEAN_UP;
    }

    serv_addr_ip4->sin_family = AF_INET;
    serv_addr_ip4->sin_addr.s_addr   =  htonl(INADDR_ANY);
    serv_addr_ip4->sin_port   = htons(80);

    listen_sock = socket(AF_INET, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0) {
        ESP_LOGE(TAG, "error in socket (%d)", errno);
		vTaskDelete(NULL);
		return;
    }

    // Enable SO_REUSEADDR to allow binding to the same
    // address and port when restarting the server
    if (setsockopt(listen_sock, 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);
    }

    ESP_LOGI(TAG, "Socket created for HTTP Server");

	// Bind
	err = bind(listen_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
	if (err != 0) {
		ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
		ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
		goto CLEAN_UP;
	}
	ESP_LOGI(TAG, "Socket bound, port %d", 80);

	// Listen
	err = listen(listen_sock, 1);
	if (err != 0) {
		ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
		goto CLEAN_UP;
	}


    while (!shutting_down) {
		struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
		uint addr_len = sizeof(source_addr);

	    // Initialize the fd read_set
	    FD_ZERO(&read_set);
	    FD_SET(listen_sock, &read_set);

	    // control sock
	    if (ctrl_fd>=0)
	    		FD_SET(ctrl_fd, &read_set);

	    max_sock =  MAX(listen_sock, ctrl_fd);

		// Call select().  Blocked until there is a http request come in at "listen_sock" or a control message come in at "ctrl_fd".
		ESP_LOGE(TAG, "Calling select()");
		rc = select(max_sock + 1, &read_set, NULL, NULL, NULL);
		ESP_LOGE(TAG, "Came out from select() with rc= %d", rc);
	    if (rc < 0)
	    {
	    		ESP_LOGE(TAG, "select() failed");
	    		break;
	    }
	    if (rc >= 1)
	    {
	    		// Do we have a control message?
	    	    if (FD_ISSET(ctrl_fd, &read_set)) {
	    	    	 	 ESP_LOGI(TAG, "processing ctrl message");
	    	    	 	 int msg;
	    	    	 	 int ret = recv(ctrl_fd, &msg, sizeof(msg), 0);
	    	    	     if (ret <= 0) {
	    	    	         ESP_LOGW(TAG, "error in recv (%d)", errno);
	    	    	     }
	    	    	     else if (ret != sizeof(msg)) {
	    	    	         ESP_LOGW(TAG, "incomplete msg");
	    	    	     }
	    	    	     else if (msg == CTRL_MSG_SHUTDOWN) {
	    	    	 		 shutting_down = true;
	    	    	 		 break;
	    	    	 	 }
	    	    }

	    	    // Do we have real http request?
	    	    if (!shutting_down && FD_ISSET(listen_sock, &read_set)) {

				// Accept
	    	    		ESP_LOGI(TAG, "About to accept connection");
				int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
				if (sock < 0) {
					ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
					break;

				}
				ESP_LOGI(TAG, "Accepted connection");

				// Timeout in 5 seconds for recv() and write()
				timeout.tv_sec = 5;
				timeout.tv_usec = 0;
			    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
			    setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));

				// Convert ip address to string
				inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
				ESP_LOGI(TAG, "HTTP Server socket accepted ip address: %s", addr_str);

				http_server_sock_serve(sock);

				shutdown(sock, 0);
				close(sock);
	    	 	}
	    }
	}

	CLEAN_UP:
		ESP_LOGE(TAG, "Shutting down web server....");

		if (ctrl_fd>=0)
			cs_free_ctrl_sock(ctrl_fd);

		if (listen_sock>=0)
			close(listen_sock);

	    shutting_down = false;
	    vTaskDelete(NULL);
}



// ====================================================================================================
void stop_http_server(TaskHandle_t httpServerTaskHandle) {

    shutting_down = true;
	int msg = CTRL_MSG_SHUTDOWN;
    cs_send_to_ctrl_sock(ctrl_fd, CTRL_PORT, &msg, sizeof(msg));

    ESP_LOGD(TAG, "Sent control msg to stop HTTP Server");
}


Who is online

Users browsing this forum: No registered users and 49 guests