How to youse SO_REUSEADDR in ESP32 arduino-framwork

Hello there,

I am currently working on a TCP server that will run on ESp32 and ESp32-S3.

This should only run for a few seconds and then process another task. This should repeat every minute.

I have managed this so far, but unfortunately I get the error “bind error: Address already in use” every time I switch on the server. I then added the socket option “SO_REUSEADDR”, but unfortunately the error still occurs.

Question, is it possible to enable the setsockopt() - option “SO_REUSEADDR” with the framework=arduino?

Unfortunately the framework was given to me by my internship company.

Sounds like you’re running into https://github.com/espressif/esp-idf/issues/6394#issuecomment-762218598?

Do you have a minimal code that reproduces the issue?

bool Anmeldeserver(){
SA_IN	srvaddr;
    int	sockfd, connfd, client_addr;
    char client_message;
    
    NodeID_Lock= xSemaphoreCreateMutex();  // crete a mutex object

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket error");
    return(EXIT_FAILURE);
    }

    bzero(&srvaddr, sizeof(srvaddr));
    srvaddr.sin_family	=AF_INET;
    srvaddr.sin_port	=htons(SRVPORT_ANMELDUNG);
    srvaddr.sin_addr.s_addr	=htonl(INADDR_ANY);

    uint8_t optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(uint8_t));
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (const void *)&optval, sizeof(uint8_t));


    struct timeval tv;
    tv.tv_sec =  5;                   //timeout_in_seconds
    tv.tv_usec = 0;                   //timeout_in_microseconds
    setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

    if (bind(sockfd, (SA *) &srvaddr, sizeof(srvaddr)) < 0) {
    perror("bind error");
    return(EXIT_FAILURE);
    }

    if (listen(sockfd, LISTENQ) < 0) {
    perror("listen error");
    return(EXIT_FAILURE);
    }
    printf("IntDaytime TCP server lauft auf port %d\n", SRVPORT_ANMELDUNG);


    while(1){

        if ((connfd = accept(sockfd, (struct sockaddr *)&client_addr, NULL)) < 0) {
        perror("accept error");
        return(EXIT_FAILURE);
        }
        
        xTaskCreate(Anmelde_handle,"TCP_Server_Komm",8000,(void*)connfd,tskIDLE_PRIORITY,NULL);



    }
    shutdown(sockfd,0);
    shutdown(connfd,0);
    if (close(connfd) < 0) {
        perror("connfd close error");
        return(EXIT_FAILURE);
    }
    if (close(sockfd) < 0) {
        perror("connfd close error");
        return(EXIT_FAILURE);
    }
    

    return(EXIT_SUCCESS);
}

/*-------------------------------------------------------------------------------------------------------------------------------------------------
Task Handle für Multiple Client Kommunikation 
-------------------------------------------------------------------------------------------------------------------------------------------------*/


void Anmelde_handle(void *connfd){
    int task_connfd;
    char client_message;
    task_connfd = (int) connfd;

    if (read(task_connfd, &client_message,1) != 1) {
        perror("Lesefehler");
        return;
    }

    if (xSemaphoreTake (NodeID_Lock, portMAX_DELAY)) {  // take the mutex
    
    struct sockaddr_in addr;
    socklen_t addr_size = sizeof(struct sockaddr_in);
    uint8_t x = atoi(&client_message);
    int res = getpeername(task_connfd, (struct sockaddr *)&addr, &addr_size);
    strcpy(NodeID[x], inet_ntoa(addr.sin_addr));


    #if DEBUG && Server_Com 
        Serial.print("IP: ");
        Serial.print(NodeID[x]);
        Serial.print(" from client: ");
        Serial.println(client_message);
    #endif

    xSemaphoreGive (NodeID_Lock);  // release the mutex
    }

    char srv_msg[20]="ACK";
    if (write(task_connfd, srv_msg, sizeof(srv_msg)) != sizeof(srv_msg)) {
        perror("write error");
        return;
    }
    shutdown(task_connfd,0);
    if (close(task_connfd) < 0) {
        perror("connfd close error");
        return;
    }
    
    #if DEBUG && TaskStackSize
    Serial.print("StackHighWaterMark: ");
    Serial.println(uxTaskGetStackHighWaterMark(NULL));
    #endif
    
    vTaskDelete( NULL );
}

This is my server code.
I open the server as a function from the loop(), after 5 seconds idle, it cancels and the loop() turns on a lamp for 5 seconds

My platformio.ini:

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino

  1. Did you try inserting
    int xTrueValue = 1;
    setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, ( void * ) &xTrueValue, sizeof( xTrueValue ) );

after your already existing setsockopt() call?

  1. The above code are just 2 functions, to make sure we’re seeing the same things and test the same things, this needs ot be a complete + minimal code, but it’s missing setup() and loop() and the includes.
  2. A general observation is that the code is not very Arduino-y. It uses the socket API provided by lwIP and not WiFiServer, WiFiClient objects and library, with which one would typically program a server on an Arduino. See e.g. https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/SimpleWiFiServer/SimpleWiFiServer.ino. The code reads more like it was ripped from a Linux reference program with a few Arduino printlns and FreeRTOS calls sprinkled in between. I’m sure the libraries would handle a few things for you that you would not need to do manually.
  3. The bool Anmeldeserver() returns non boolean values casted to bool very often, such as return(EXIT_FAILURE);. Since EXIT_FAILURE evlauates to 8 normally, and 8 is a truthy value since it’s not 0, the function would return true when it’s encountered a fatal error. This is very counter-intuitive, as bool return values typically mean true = it worked, false = fail.