Arm Mbed OS support forum

HTTP-Server based on TCPSocket (mbed os 6!)

hi im searching for a working example where my target acts as server (read incoming, answer) for a restful http server.

wherever you look you find deprectated stuff that does not work anymore.

do you have a minimal example on how to run a restful server for mbed os 6, not 5.

Hello Jibba,

Provided your target supports the Mbed’s EMAC (Ethernet Phy) driver the following “Web server echo” example should work with Mbed OS 6:

#include "mbed.h"
#include "EthernetInterface.h"
#include "TCPSocket.h"

#define IP      "192.168.1.181"
#define GATEWAY "192.168.1.1"
#define NETMASK "255.255.255.0"
#define PORT    80

EthernetInterface*  net;

TCPSocket           server;
TCPSocket*          clientSocket;
SocketAddress       clientAddress;
char                rxBuf[512] = { 0 };
char                txBuf[512] = { 0 };

int main(void)
{
    printf("Starting\r\n");

    net = new EthernetInterface;

    if (!net) {
        printf("Error! No network inteface found.\n");
        return 0;
    }

    //net->set_network (IP, NETMASK, GATEWAY);  // include to use static IP address
    nsapi_size_or_error_t   r = net->connect();
    if (r != 0) {
        printf("Error! net->connect() returned: %d\n", r);
        return r;
    }

    // Show the network address
    SocketAddress   ip;
    SocketAddress   netmask;
    SocketAddress   gateway;

    net->get_ip_address(&ip);
    net->get_netmask(&netmask);
    net->get_gateway(&gateway);

    ip.set_port(PORT);

    const char*     ipAddr = ip.get_ip_address();
    const char*     netmaskAddr = netmask.get_ip_address();
    const char*     gatewayAddr = gateway.get_ip_address();

    printf("IP address: %s\r\n", ipAddr ? ipAddr : "None");
    printf("Netmask: %s\r\n", netmaskAddr ? netmaskAddr : "None");
    printf("Gateway: %s\r\n\r\n", gatewayAddr ? gatewayAddr : "None");

    /* Open the server on ethernet stack */
    server.open(net);

    /* Bind the HTTP port (TCP 80) to the server */
    server.bind(ip);

    /* Can handle 5 simultaneous connections */
    server.listen(5);

    //listening for http GET request
    while (true) {
        printf("=========================================\r\n");

        nsapi_error_t   error = 0;

        clientSocket = server.accept(&error);
        if (error != 0) {
            printf("Connection failed!\r\n");
        }
        else {
            //clientSocket->set_timeout(200);
            clientSocket->getpeername(&clientAddress);
            printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
            error = clientSocket->recv(rxBuf, sizeof(rxBuf));

            switch (error) {
                case 0:
                    printf("Recieved buffer is empty.\r\n");
                    break;

                case -1:
                    printf("Failed to read data from client.\r\n");
                    break;

                default:
                    printf("Recieved Data: %d\n\r\n\r%.*s\r\n\n\r", strlen(rxBuf), strlen(rxBuf), rxBuf);
                    if (rxBuf[0] == 'G' && rxBuf[1] == 'E' && rxBuf[2] == 'T') {
                        //setup http response header & data
                        sprintf
                        (
                            txBuf,
                            "HTTP/1.1 200 OK\nContent-Length: %d\r\nContent-Type: text\r\nConnection: Close\r\n\r\n",
                            strlen(rxBuf)
                        );
                        strcat(txBuf, rxBuf);
                        clientSocket->send(txBuf, strlen(txBuf));

                        // printf("------------------------------------\r\n");
                        // printf("Sent:\r\n%s\r\n", txBuf);
                        printf("echo done.\r\n");
                    }
                    break;
            }
        }

        clientSocket->close();
        printf("Client socket closed\r\n");
    }
}

To test the program type the Web Server’s IP address, printed to the serial console at start up, to your web browser and hit enter.

thx

Hello, I came across this post and tried your code. It works fine as is, but if I try to use it in a thread it keeps getting stuck due to a NSAPI_ERROR_WOULD_BLOCK condition in the recv() function. Strangely enough this only happens after the webpage is reloaded after the first time. So in other words, it works for the first time but gives issues after that. As far as I can find online the socket should be blocking by default and manually setting it to blocking also doesn’t fix it. Do you have any clue why this would happen?

  • What do you mean by “if I try to use it in a thread …”?

  • The example above is waiting in the main thread to accept a connection on a socket:

while (true) {
...
    clientSocket = server.accept(&error);
...

    clientSocket->close();
...
}

Mbed explains the accept function as follows:

    /** Accepts a connection on a socket.
     *
     *  The server socket must be bound and set to listen for connections.
     *  On a new connection, returns connected network socket which user is expected to call close()
     *  and that deallocates the resources. Referencing a returned pointer after a close()
     *  call is not allowed and leads to undefined behavior.
     *
     *  By default, accept blocks until incoming connection occurs. If socket is set to
     *  non-blocking or times out, error is set to NSAPI_ERROR_WOULD_BLOCK.
     *
     *  @param error      pointer to storage of the error value or NULL:
     *                    NSAPI_ERROR_OK on success
     *                    NSAPI_ERROR_WOULD_BLOCK if socket is set to non-blocking and would block
     *                    NSAPI_ERROR_NO_SOCKET if the socket was not open
     *  @return           pointer to a socket
     */
    TCPSocket *accept(nsapi_error_t *error = NULL) override;

First of all, thanks for your fast response!

By using it in a thread I meant that I put all the code from the main function inside a new function called “web_server”. The main function is replaced by an almost empty one which is used to start a new thread that executes this new “web_server” function. Apart from starting the “web_server” thread, the main only has the infinite while loop left. I also tried adding a blinky thread and that kept running fine.

The accept function seems to be working fine. It’s the recv function that is causing the issues and hangs at the NSAPI_ERROR_WOULD_BLOCK condition.

I moved the web server to a webServerTask running in a webServerThread. At the same time, an LED is toggled in the main task (which is running in the main thread).
No errors were reported when trying to connect to the web server multiple times:

#include "mbed.h"
#include "EthernetInterface.h"
#include "TCPSocket.h"

#define IP      "192.168.1.181"
#define GATEWAY "192.168.1.1"
#define NETMASK "255.255.255.0"
#define PORT    80

DigitalOut          led1(LED1);

EthernetInterface*  net;
TCPSocket           server;
TCPSocket*          clientSocket;
SocketAddress       clientAddress;
char                rxBuf[512] = { 0 };
char                txBuf[512] = { 0 };

Thread              webServerThread;
void                webServerTask();

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void webServerTask()
{
    net = new EthernetInterface;

    if (!net) {
        printf("Error! No network inteface found.\n");
        return;
    }

    //net->set_network (IP, NETMASK, GATEWAY);  // include to use static IP address
    nsapi_size_or_error_t   r = net->connect();
    if (r != 0) {
        printf("Error! net->connect() returned: %d\n", r);
        return;
    }

    // Show the network address
    SocketAddress   ip;
    SocketAddress   netmask;
    SocketAddress   gateway;

    net->get_ip_address(&ip);
    net->get_netmask(&netmask);
    net->get_gateway(&gateway);

    ip.set_port(PORT);

    const char*     ipAddr = ip.get_ip_address();
    const char*     netmaskAddr = netmask.get_ip_address();
    const char*     gatewayAddr = gateway.get_ip_address();

    printf("IP address: %s\r\n", ipAddr ? ipAddr : "None");
    printf("Netmask: %s\r\n", netmaskAddr ? netmaskAddr : "None");
    printf("Gateway: %s\r\n\r\n", gatewayAddr ? gatewayAddr : "None");

    /* Open the server on ethernet stack */
    server.open(net);

    /* Bind the HTTP port (TCP 80) to the server */
    server.bind(ip);

    /* Can handle 5 simultaneous connections */
    server.listen(5);

    //listening for http GET request
    while (true) {
        printf("=========================================\r\n");

        nsapi_error_t   error = 0;

        clientSocket = server.accept(&error);
        if (error != 0) {
            printf("Connection failed!\r\n");
        }
        else {
            //clientSocket->set_timeout(200);
            clientSocket->getpeername(&clientAddress);
            printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
            error = clientSocket->recv(rxBuf, sizeof(rxBuf));

            switch (error) {
                case 0:
                    printf("Recieved buffer is empty.\r\n");
                    break;

                case -1:
                    printf("Failed to read data from client.\r\n");
                    break;

                default:
                    printf("Recieved Data: %d\n\r\n\r%.*s\r\n\n\r", strlen(rxBuf), strlen(rxBuf), rxBuf);
                    if (rxBuf[0] == 'G' && rxBuf[1] == 'E' && rxBuf[2] == 'T') {
                        //setup http response header & data
                        sprintf
                        (
                            txBuf,
                            "HTTP/1.1 200 OK\nContent-Length: %d\r\nContent-Type: text\r\nConnection: Close\r\n\r\n",
                            strlen(rxBuf)
                        );
                        strcat(txBuf, rxBuf);
                        clientSocket->send(txBuf, strlen(txBuf));

                        // printf("------------------------------------\r\n");
                        // printf("Sent:\r\n%s\r\n", txBuf);
                        printf("echo done.\r\n");
                    }
                    break;
            }
        }

        clientSocket->close();
        printf("Client socket closed\r\n");
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int main(void)
{
    printf("Starting\r\n");
    webServerThread.start(callback(webServerTask));

    while (true) {
        led1 = !led1;
        ThisThread::sleep_for(200ms);
    }
}

Thanks for the help so far!

Your code is very similar to the code that I used. I only uncommented the line that gives the server a static IP . Unfortunately it still hangs occasionally (browser keeps showing the spinning loading icon). I tried the version with only the main function again and I noticed that that also has it, so it’s not just this additional thread that’s causing it like I previously thought. It didn’t occur when testing this version previously, so sorry for that… So in other words, both versions have it. However, setting the timeout to 200 (uncommenting the line) makes the issue almost a non-issue. Without this line uncommented the user has to refresh the page if it gets stuck or wait like 90 seconds.

Unfortunately I started to notice a worse problem. Whenever the user refreshes too fast (so for example spams the F5 key) it will result in a HardFault which makes the server unavailable until the board is reset. This happens on my STM32F746ZG board at least. Do you also experience this on your board?

No matter how hard I was trying to reproduce the failure on my LPC1768 the server always worked correctly. It seems to be something specific to the STM32F746ZG (or maybe STM targets).