A very basic WebSocket Server

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 12:22 am

Hiho,

I wrote a very basic WebSocket Server for the ESP32. It is far from perfect, the code is ugly, it does not handle any special cases but I think a good starting point and it works fine with my environment (Chrome).

Next I will check if I need to implement "ping" for WebSockets and then I will try to send data from the server to the browser(Client)

Code: Select all

/**
 * \file   WebSocket_Task.c
 * \author   Thomas Barth
 * \date    16.01.2017
 * \version   0.1
 *
 * \brief Global definitions for datatypes and definitions
 *
 * \warning This software is a PROTOTYPE version and is not designed or intended for use in production, especially not for safety-critical applications!
 * The user represents and warrants that it will NOT use or redistribute the Software for such purposes.
 * This prototype is for research purposes only. This software is provided "AS IS," without a warranty of any kind.
 */


#include "WebSocket_Task.h"

#include "freertos/FreeRTOS.h"
#include "esp_heap_alloc_caps.h"
#include "hwcrypto/sha.h"
#include "esp_system.h"
#include "lwip/API.h"

#include <string.h>

#define WS_CLIENT_KEY_L   24
#define SHA1_RES_L      20
#define WS_PORT         9998

//buffer for sending data to the client
char out_buf[200];

char WS_sec_WS_keys[]="Sec-WebSocket-Key:";
char WS_sec_conKey[]="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                '4', '5', '6', '7', '8', '9', '+', '/'};

static int mod_table[] = {0, 2, 1};

char *base64_encode(char *data, uint8_t input_length,  uint16_t *output_length) {

   uint8_t i,j;

    *output_length = 4 * ((input_length + 2) / 3);

    char *encoded_data = malloc(*output_length);
    if (encoded_data == NULL) return NULL;

    for (i = 0, j = 0; i < input_length;) {

        uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;

        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;

        encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
    }

    for (i = 0; i < mod_table[input_length % 3]; i++)
        encoded_data[*output_length - 1 - i] = '=';

    return encoded_data;
}

static void ws_server_netconn_serve(struct netconn *conn) {

   //Netbuf
   struct netbuf *inbuf;

   //message buffer
   char *buf;

   //number of received bytes from netbuf
   uint16_t buflen;

   //pointer to buffer (multi purpose)
   char* p_buf;

   //Client Sec Key buffer
   char __SHA1_Inp[WS_CLIENT_KEY_L+sizeof(WS_sec_conKey)];

   //SHA1 result
   char result[SHA1_RES_L];

   //multi purpose number buffer
   uint16_t i;

   //length of received payload
   uint16_t payload_length;

   //will point to the received payload
   char* p_payload;

   //receive handshake request
   if(netconn_recv(conn, &inbuf)==ERR_OK)   {

      //read buffer
      netbuf_data(inbuf, (void**) &buf, &buflen);

      //write static key into SHA1 Input
      for(i=0;i<sizeof(WS_sec_conKey);i++)
         __SHA1_Inp[i+WS_CLIENT_KEY_L]=WS_sec_conKey[i];

      //find Client Sec-WebSocket-Key:
      p_buf=strstr(buf, WS_sec_WS_keys);

      //check if needle "Sec-WebSocket-Key:" was found
      if(p_buf!=NULL){

         //get Client Key
         for(i=0;i<WS_CLIENT_KEY_L;i++)
            __SHA1_Inp[i]=*(p_buf+sizeof(WS_sec_WS_keys)+i);

         // calculate hash
         esp_sha(SHA1,(unsigned char*)__SHA1_Inp,strlen(__SHA1_Inp),(unsigned char*)result);

         //hex to base64
         p_buf =base64_encode(result, SHA1_RES_L,&i);

         //send handshake
         sprintf(out_buf,"HTTP/1.1 101 Switching Protocols \r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %.*s\r\n\r\n", i, p_buf);
         netconn_write(conn, out_buf, strlen(out_buf),   NETCONN_COPY);

         //free base 64 encoded sec key
         free(p_buf);

         //Wait for new data
         while(netconn_recv(conn, &inbuf)==ERR_OK){

            //read data from inbuf
            netbuf_data(inbuf, (void**) &buf, &buflen);

            /*
             * get payload length.
             * The playload is the uint in the 7 LSBs of the second byte.
             * If the value is smaller than 126, the length can directly be read from the byte.
             * If the value is 126, the playload length is in byte 2 and 3.
             * If the value is 127, the playload length is in byte 2, 3, 4, 5, 6, 7, 8 and 9
             */
            if((buf[1]&(~BIT(7)))<126){
               //get payload length
               payload_length=(buf[1]&(~BIT(7)));
               //set pointer to first mask byte
               p_buf=&buf[2];
            }
            else if((buf[1]&(~BIT(7)))==126){
               //get payload length
               payload_length=buf[2]<<8|buf[3];
               //set pointer to first mask byte
               p_buf=&buf[4];
            }
            else{
               //we will not handle messages longer than 2^16 bytes on an embedded target! ;)
               payload_length=0;

               break;
            }

            //allocate memory for decoded message
            p_payload = pvPortMallocCaps(payload_length,MALLOC_CAP_8BIT);

            //check if malloc succeeded
            if(p_payload!=NULL){

               //decode playload
               for (i = 0; i < payload_length; i++) {
                  p_payload[i] = (p_buf+4)[i] ^ p_buf[i % 4];
               }

               //do stuff
               printf("content %.*s\n",payload_length,p_payload);

               //free payload buffer
               free(p_payload);
            }

            //free input buffer
            netbuf_delete(inbuf);
         }//while(netconn_recv(conn, &inbuf)==ERR_OK)
      }//check if needle "Sec-WebSocket-Key:" was found
   }//receive handshake

   //delete buffer
   netbuf_delete(inbuf);

   // Close the connection
   netconn_close(conn);

   //Delete connection
   netconn_delete(conn);

}

void ws_server(void *pvParameters){
  struct netconn *conn, *newconn;
  esp_err_t err;
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, NULL, WS_PORT);
  netconn_listen(conn);

  while(netconn_accept(conn, &newconn)==ESP_OK)
     ws_server_netconn_serve(newconn);

   netconn_close(conn);
   netconn_delete(conn);
}
Last edited by ThomasB on Mon Jan 16, 2017 8:35 pm, edited 1 time in total.

User avatar
rudi ;-)
Posts: 1134
Joined: Fri Nov 13, 2015 3:25 pm

Re: A very basic WebSocket Server

Postby rudi ;-) » Mon Jan 16, 2017 5:23 pm

ThomasB wrote:Hiho,

I wrote a very basic WebSocket Server for the ESP32. It is far from perfect, the code is ugly, it does not handle any special cases but I think a good starting point and it works fine with my environment (Chrome).


Code: Select all

/**

#include "WebSocket_Task.h"
..
..
#include "CANopen_Client.h"
..




hohi tom,

txs for share - looks promising and i had a try

Code: Select all


void ws_server(void *pvParameters){
  struct netconn *conn, *newconn;
  esp_err_t err;
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, NULL, WS_PORT);
  netconn_listen(conn);

  while(netconn_accept(conn, &newconn)==ESP_OK)
     ws_server_netconn_serve(newconn);

   printf("\nhuh!... did not accept\n");
   printf("\ntry to close connection\n");
   netconn_close(conn);
   printf("\ntry to delete connection\n");
   netconn_delete(conn);
}

void app_main() {
   printf("\ntry to start webserver..\n");
   ..
   }



try to start webserver..
(696) cpu_start: Starting scheduler on APP CPU.
netconn_bind: invalid connnet
conn_listen: invalid connnet
conn_accept: invalid conn

huh!... did not accept
try to close connection
netconn_close: invalid conn
try to delete connection


- do we miss something ( two headers ? )
( IP Addr, Protocol[tcp, udp], )
- with which parameters you start the websvr task?

best wishes
rudi ;-)


Code: Select all

void tcpip_adapter_init(void)
{
    static bool tcpip_inited = false;

    if (tcpip_inited == false) {
        tcpip_inited = true;

        tcpip_init(NULL, NULL);

        IP4_ADDR(&esp_ip[TCPIP_ADAPTER_IF_AP].ip, 192, 168 , 4, 1);
        IP4_ADDR(&esp_ip[TCPIP_ADAPTER_IF_AP].gw, 192, 168 , 4, 1);
        IP4_ADDR(&esp_ip[TCPIP_ADAPTER_IF_AP].netmask, 255, 255 , 255, 0);
    }
}

-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

Re: A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 7:20 pm

Howdy.

Strange...
Have you defined WS_PORT?

CANopen is irrelevant, that's stuff from the application layer.

I continued working on it, so it looks a little different now. For now I will only handle frames with <126byte, as this is sufficient for my application (which will only use Chrome).
main:

Code: Select all

    //Create Websocket Server Task
    xTaskCreate(&ws_server, "ws_server", 2048, NULL, 5, NULL);


WebSocket_Task.h

Code: Select all

#ifndef MAIN_WEBSOCKET_TASK_H_
#define MAIN_WEBSOCKET_TASK_H_


void ws_server(void *pvParameters);


#endif /* MAIN_WEBSOCKET_TASK_H_ */


WebSocket_Task.c

Code: Select all

/**
 * \file   WebSocket_Task.c
 * \author   Thomas Barth
 * \date    16.01.2017
 * \version   0.1
 *
 * \brief A very basic and hacky WebSocket-Server
 *
 * \warning This software is a PROTOTYPE version and is not designed or intended for use in production, especially not for safety-critical applications!
 * The user represents and warrants that it will NOT use or redistribute the Software for such purposes.
 * This prototype is for research purposes only. This software is provided "AS IS," without a warranty of any kind.
 */


#include "WebSocket_Task.h"

#include "freertos/FreeRTOS.h"
#include "esp_heap_alloc_caps.h"
#include "hwcrypto/sha.h"
#include "esp_system.h"
#include "base64.h"
#include "wpa2/utils/base64.h"
#include <string.h>

#define WS_PORT            9998   //TCP Port for the Server
#define WS_CLIENT_KEY_L      24      //Length of the Client Key
#define SHA1_RES_L         20      //SHA1 result
#define WS_MASK_L         0x4      //Length of MASK field in WebSocket HEader
#define WS_STD_LEN         125      //Maximum Length of standard length frames
#define WS_SPRINTF_ARG_L   4      //Length of sprintf argument for string (%.*s)

//Opcode according to RFC 6455
typedef enum{
   WS_OP_CON=0x0,               //Continuation Frame
   WS_OP_TXT=0x1,               //Text Frame
   WS_OP_BIN=0x2,               //Binary Frame
   WS_OP_CLS=0x8,               //Connection Close Frame
   WS_OP_PIN=0x9,               //Ping Frame
   WS_OP_PON=0xa               //Pong Frame
}WS_OPCODES;

typedef struct{
   uint8_t      opcode:WS_MASK_L;
   uint8_t      reserved:3;
   uint8_t      FIN:1;
   uint8_t      payload_length:7;
   uint8_t      mask:1;
}WS_frame_header_t ;

//stores open WebSocket connections
static struct netconn* WS_conn=NULL;

const char WS_sec_WS_keys[]="Sec-WebSocket-Key:";
const char WS_sec_conKey[]="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const char WS_srv_hs[]="HTTP/1.1 101 Switching Protocols \r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %.*s\r\n\r\n";

err_t WS_write_data(char* p_data, size_t length){

   //check if we have an open connection
   if(WS_conn==NULL)
      return ERR_CONN;

   //currently only frames with a payload length <WS_STD_LEN are supported
   if(length>WS_STD_LEN)
      return ERR_VAL;

   //netconn_write result buffer
   err_t   result;

   //prepare header
   WS_frame_header_t hdr;
   hdr.FIN=0x1;
   hdr.payload_length=length;
   hdr.mask=0;
   hdr.reserved=0;
   hdr.opcode=WS_OP_TXT;

   //send header
   result=netconn_write(WS_conn, &hdr, sizeof(WS_frame_header_t), NETCONN_COPY);

   //check if header had been send
   if(result!=ERR_OK)
      return result;

   //send payload
   return netconn_write(WS_conn, p_data, length, NETCONN_COPY);
}

static void ws_server_netconn_serve(struct netconn *conn) {

   //Netbuf
   struct netbuf *inbuf;

   //message buffer
   char *buf;

   //pointer to buffer (multi purpose)
   char* p_buf;

   //Pointer to SHA1 input
   char* p_SHA1_Inp;

   //Pointer to SHA1 result
   char* p_SHA1_result;

   //multi purpose number buffer
   uint16_t i;

   //will point to payload (send and receive
   char* p_payload;

   //Frame header pointer
   WS_frame_header_t* p_frame_hdr;

   //allocate memory for SHA1 input
   p_SHA1_Inp=pvPortMallocCaps(WS_CLIENT_KEY_L+sizeof(WS_sec_conKey),MALLOC_CAP_8BIT);

   //allocate memory for SHA1 result
   p_SHA1_result=pvPortMallocCaps(SHA1_RES_L,MALLOC_CAP_8BIT);

   //Check if malloc suceeded
   if((p_SHA1_Inp!=NULL)&&(p_SHA1_result!=NULL)){

      //receive handshake request
      if(netconn_recv(conn, &inbuf)==ERR_OK)   {

         //read buffer
         netbuf_data(inbuf, (void**) &buf, &i);

         //write static key into SHA1 Input
         for(i=0;i<sizeof(WS_sec_conKey);i++)
            p_SHA1_Inp[i+WS_CLIENT_KEY_L]=WS_sec_conKey[i];

         //find Client Sec-WebSocket-Key:
         p_buf=strstr(buf, WS_sec_WS_keys);

         //check if needle "Sec-WebSocket-Key:" was found
         if(p_buf!=NULL){

            //get Client Key
            for(i=0;i<WS_CLIENT_KEY_L;i++)
               p_SHA1_Inp[i]=*(p_buf+sizeof(WS_sec_WS_keys)+i);

            // calculate hash
            esp_sha(SHA1,(unsigned char*)p_SHA1_Inp,strlen(p_SHA1_Inp),(unsigned char*)p_SHA1_result);

            //hex to base64
            p_buf =(char*)_base64_encode((unsigned char*)p_SHA1_result, SHA1_RES_L,(size_t*)&i);

            //free SHA1 input
            free(p_SHA1_Inp);

            //free SHA1 result
            free(p_SHA1_result);

            //allocate memory for handshake
            p_payload = pvPortMallocCaps(sizeof(WS_srv_hs)+i-WS_SPRINTF_ARG_L,MALLOC_CAP_8BIT);

            //check if malloc suceeded
            if(p_payload!=NULL){

               //prepare handshake
               sprintf(p_payload,WS_srv_hs, i-1, p_buf);

               //send handshake
               netconn_write(conn, p_payload, sizeof(WS_srv_hs)+i-WS_SPRINTF_ARG_L,   NETCONN_COPY);

               //free base 64 encoded sec key
               free(p_buf);

               //free handshake memory
               free(p_payload);

               //set pointer to open WebSocket connection
               WS_conn=conn;

               //Wait for new data
               while(netconn_recv(conn, &inbuf)==ERR_OK){

                  //read data from inbuf
                  netbuf_data(inbuf, (void**) &buf, &i);

                  //get pointer to header
                  p_frame_hdr=(WS_frame_header_t*)buf;

                  //check if clients wants to close the connection
                  if(p_frame_hdr->opcode==WS_OP_CLS)
                     break;

                  //get payload length
                  if(p_frame_hdr->payload_length<=WS_STD_LEN){

                     //get beginning of mask or payload
                     p_buf=(char*)&buf[sizeof(WS_frame_header_t)];

                     //check if content is masked
                     if(p_frame_hdr->mask){

                        //allocate memory for decoded message
                        p_payload = pvPortMallocCaps(p_frame_hdr->payload_length,MALLOC_CAP_8BIT);

                        //check if malloc succeeded
                        if(p_payload!=NULL){

                           //decode playload
                           for (i = 0; i < p_frame_hdr->payload_length; i++)
                              p_payload[i] = (p_buf+WS_MASK_L)[i] ^ p_buf[i % WS_MASK_L];
                        }
                     }
                     else
                        //content is not masked
                        p_payload=p_buf;

                     //do stuff
                     if((p_payload!=NULL)&&(p_frame_hdr->opcode==WS_OP_TXT))
                        printf("content %.*s\n",p_frame_hdr->payload_length,p_payload);

                     //free payload buffer
                     if(p_frame_hdr->mask&&p_payload!=NULL)
                        free(p_payload);

                  }//p_frame_hdr->payload_length<126

                  //free input buffer
                  netbuf_delete(inbuf);

               }//while(netconn_recv(conn, &inbuf)==ERR_OK)
            }//p_payload!=NULL
         }//check if needle "Sec-WebSocket-Key:" was found
      }//receive handshake
   }//p_SHA1_Inp!=NULL&p_SHA1_result!=NULL

   //release pointer to open WebSocket connection
   WS_conn=NULL;

   //delete buffer
   netbuf_delete(inbuf);

   // Close the connection
   netconn_close(conn);

   //Delete connection
   netconn_delete(conn);

}

void ws_server(void *pvParameters){
  struct netconn *conn, *newconn;
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, NULL, WS_PORT);
  netconn_listen(conn);

  while(netconn_accept(conn, &newconn)==ERR_OK)
     ws_server_netconn_serve(newconn);

   netconn_close(conn);
   netconn_delete(conn);
}



ESP32.html (for testing)

Code: Select all

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <script type="text/javascript">
   
      var ws;
      
       if ("WebSocket" in window){ 
            ws = new WebSocket("ws://192.168.4.1:9998");
         
         ws.onopen = function(){
           alert("Connection is open");
         };
         
         ws.onmessage = function (evt)
         {
            var received_msg = evt.data;
            alert("Message is received...");
         };

         ws.onclose = function()
         {
            // websocket is closed.
            alert("Connection is closed...");
         };
      }else{
            alert("WebSocket NOT supported by your Browser!");
      }
                  
        function WebSocketSend(){
         //setInterval(myTimer, 100);
         var d = new Date();
         ws.send( d.toLocaleTimeString());
        }
      
      function myTimer() {
         var d = new Date();
         ws.send( d.toLocaleTimeString());
      }
      
      </script>
      
   </head>
   <body>
     
      <div id="sse">
         <a href="javascript:WebSocketSend()">send WebSocket</a>
      </div>
     
   </body>
</html>



Thats all I have :lol: (only minor changes as I can not publish certain includes etc. but I tested this version successfully)

If this doesn't work, I can create a small project and upload it, though this is very proprietary and hacky ;)
Last edited by ThomasB on Tue Jan 17, 2017 7:31 pm, edited 1 time in total.

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

Re: A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 9:17 pm

Update, now with the possibility to send frames to the client (without that masking nonsense...).

Thats so far all I need :ugeek: I maybe will optimize (like out_buf on the stack) a few things but basically Im done for my part...

WebSocket_Task.h

Code: Select all

#ifndef MAIN_WEBSOCKET_TASK_H_
#define MAIN_WEBSOCKET_TASK_H_

#include "lwip/API.h"

err_t WS_write_data(char* p_data, size_t length);

void ws_server(void *pvParameters);


#endif /* MAIN_WEBSOCKET_TASK_H_ */


WebSocket_Task.c

Code: Select all

/**
 * \file   WebSocket_Task.c
 * \author   Thomas Barth
 * \date    16.01.2017
 * \version   0.1
 *
 * \brief A very basic and hacky WebSocket-Server
 *
 * \warning This software is a PROTOTYPE version and is not designed or intended for use in production, especially not for safety-critical applications!
 * The user represents and warrants that it will NOT use or redistribute the Software for such purposes.
 * This prototype is for research purposes only. This software is provided "AS IS," without a warranty of any kind.
 */


#include "WebSocket_Task.h"

#include "freertos/FreeRTOS.h"
#include "esp_heap_alloc_caps.h"
#include "hwcrypto/sha.h"
#include "esp_system.h"
#include "wpa2/utils/base64.h"
#include <string.h>

#define WS_PORT            9998   //TCP Port for the Server
#define WS_CLIENT_KEY_L      24      //Length of the Client Key
#define SHA1_RES_L         20      //SHA1 result
#define WS_MASK_L         0x4      //Length of MASK field in WebSocket HEader
#define WS_STD_LEN         125      //Maximum Length of standard length frames
#define WS_SPRINTF_ARG_L   4      //Length of sprintf argument for string (%.*s)

//Opcode according to RFC 6455
typedef enum{
   WS_OP_CON=0x0,               //Continuation Frame
   WS_OP_TXT=0x1,               //Text Frame
   WS_OP_BIN=0x2,               //Binary Frame
   WS_OP_CLS=0x8,               //Connection Close Frame
   WS_OP_PIN=0x9,               //Ping Frame
   WS_OP_PON=0xa               //Pong Frame
}WS_OPCODES;

typedef struct{
   uint8_t      opcode:WS_MASK_L;
   uint8_t      reserved:3;
   uint8_t      FIN:1;
   uint8_t      payload_length:7;
   uint8_t      mask:1;
}WS_frame_header_t ;

//stores open WebSocket connections
static struct netconn* WS_conn=NULL;

const char WS_sec_WS_keys[]="Sec-WebSocket-Key:";
const char WS_sec_conKey[]="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const char WS_srv_hs[]="HTTP/1.1 101 Switching Protocols \r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %.*s\r\n\r\n";

err_t WS_write_data(char* p_data, size_t length){

   //check if we have an open connection
   if(WS_conn==NULL)
      return ERR_CONN;

   //currently only frames with a payload length <WS_STD_LEN are supported
   if(length>WS_STD_LEN)
      return ERR_VAL;

   //netconn_write result buffer
   err_t   result;

   //prepare header
   WS_frame_header_t hdr;
   hdr.FIN=0x1;
   hdr.payload_length=length;
   hdr.mask=0;
   hdr.reserved=0;
   hdr.opcode=WS_OP_TXT;

   //send header
   result=netconn_write(WS_conn, &hdr, sizeof(WS_frame_header_t), NETCONN_COPY);

   //check if header had been send
   if(result!=ERR_OK)
      return result;

   //send payload
   return netconn_write(WS_conn, p_data, length, NETCONN_COPY);
}

static void ws_server_netconn_serve(struct netconn *conn) {

   //Netbuf
   struct netbuf *inbuf;

   //message buffer
   char *buf;

   //pointer to buffer (multi purpose)
   char* p_buf;

   //Pointer to SHA1 input
   char* p_SHA1_Inp;

   //Pointer to SHA1 result
   char* p_SHA1_result;

   //multi purpose number buffer
   uint16_t i;

   //will point to payload (send and receive
   char* p_payload;

   //Frame header pointer
   WS_frame_header_t* p_frame_hdr;

   //allocate memory for SHA1 input
   p_SHA1_Inp=pvPortMallocCaps(WS_CLIENT_KEY_L+sizeof(WS_sec_conKey),MALLOC_CAP_8BIT);

   //allocate memory for SHA1 result
   p_SHA1_result=pvPortMallocCaps(SHA1_RES_L,MALLOC_CAP_8BIT);

   //Check if malloc suceeded
   if((p_SHA1_Inp!=NULL)&&(p_SHA1_result!=NULL)){

      //receive handshake request
      if(netconn_recv(conn, &inbuf)==ERR_OK)   {

         //read buffer
         netbuf_data(inbuf, (void**) &buf, &i);

         //write static key into SHA1 Input
         for(i=0;i<sizeof(WS_sec_conKey);i++)
            p_SHA1_Inp[i+WS_CLIENT_KEY_L]=WS_sec_conKey[i];

         //find Client Sec-WebSocket-Key:
         p_buf=strstr(buf, WS_sec_WS_keys);

         //check if needle "Sec-WebSocket-Key:" was found
         if(p_buf!=NULL){

            //get Client Key
            for(i=0;i<WS_CLIENT_KEY_L;i++)
               p_SHA1_Inp[i]=*(p_buf+sizeof(WS_sec_WS_keys)+i);

            // calculate hash
            esp_sha(SHA1,(unsigned char*)p_SHA1_Inp,strlen(p_SHA1_Inp),(unsigned char*)p_SHA1_result);

            //hex to base64
            p_buf =(char*)_base64_encode((unsigned char*)p_SHA1_result, SHA1_RES_L,(size_t*)&i);

            //free SHA1 input
            free(p_SHA1_Inp);

            //free SHA1 result
            free(p_SHA1_result);

            //allocate memory for handshake
            p_payload = pvPortMallocCaps(sizeof(WS_srv_hs)+i-WS_SPRINTF_ARG_L,MALLOC_CAP_8BIT);

            //check if malloc suceeded
            if(p_payload!=NULL){

               //prepare handshake
               sprintf(p_payload,WS_srv_hs, i-1, p_buf);

               //send handshake
               netconn_write(conn, p_payload, strlen(p_payload),   NETCONN_COPY);

               //free base 64 encoded sec key
               free(p_buf);

               //free handshake memory
               free(p_payload);

               //set pointer to open WebSocket connection
               WS_conn=conn;

               //Wait for new data
               while(netconn_recv(conn, &inbuf)==ERR_OK){

                  //read data from inbuf
                  netbuf_data(inbuf, (void**) &buf, &i);

                  //get pointer to header
                  p_frame_hdr=(WS_frame_header_t*)buf;

                  //check if clients wants to close the connection
                  if(p_frame_hdr->opcode==WS_OP_CLS)
                     break;

                  //get payload length
                  if(p_frame_hdr->payload_length<=WS_STD_LEN){

                     //get beginning of mask or payload
                     p_buf=(char*)&buf[sizeof(WS_frame_header_t)];

                     //check if content is masked
                     if(p_frame_hdr->mask){

                        //allocate memory for decoded message
                        p_payload = pvPortMallocCaps(p_frame_hdr->payload_length,MALLOC_CAP_8BIT);

                        //check if malloc succeeded
                        if(p_payload!=NULL){

                           //decode playload
                           for (i = 0; i < p_frame_hdr->payload_length; i++)
                              p_payload[i] = (p_buf+WS_MASK_L)[i] ^ p_buf[i % WS_MASK_L];
                        }
                     }
                     else
                        //content is not masked
                        p_payload=p_buf;

                     //do stuff
                     if((p_payload!=NULL)&&(p_frame_hdr->opcode==WS_OP_TXT))
                        printf("content %.*s\n",p_frame_hdr->payload_length,p_payload);

                     //free payload buffer
                     if(p_frame_hdr->mask&&p_payload!=NULL)
                        free(p_payload);

                  }//p_frame_hdr->payload_length<126

                  //free input buffer
                  netbuf_delete(inbuf);

               }//while(netconn_recv(conn, &inbuf)==ERR_OK)
            }//p_payload!=NULL
         }//check if needle "Sec-WebSocket-Key:" was found
      }//receive handshake
   }//p_SHA1_Inp!=NULL&p_SHA1_result!=NULL

   //release pointer to open WebSocket connection
   WS_conn=NULL;

   //delete buffer
   netbuf_delete(inbuf);

   // Close the connection
   netconn_close(conn);

   //Delete connection
   netconn_delete(conn);

}

void ws_server(void *pvParameters){
  struct netconn *conn, *newconn;
  conn = netconn_new(NETCONN_TCP);
  netconn_bind(conn, NULL, WS_PORT);
  netconn_listen(conn);

  while(netconn_accept(conn, &newconn)==ERR_OK)
     ws_server_netconn_serve(newconn);

   netconn_close(conn);
   netconn_delete(conn);
}




New Testbench

Code: Select all

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <script type="text/javascript">
   
      var ws;
      
       if ("WebSocket" in window){ 
            ws = new WebSocket("ws://192.168.4.1:9998");
         
         ws.onopen = function(){
           alert("Connection is open");
         };
         
         ws.onmessage = function (evt)
         {
            alert("Message is received: " + evt.data);
         };

         ws.onclose = function()
         {
            // websocket is closed.
            alert("Connection is closed...");
         };
      }else{
            alert("WebSocket NOT supported by your Browser!");
      }
            
      
        function WebSocketSend(){
         //setInterval(myTimer, 100);
         var d = new Date();
         ws.send( d.toLocaleTimeString());
        }
      
      function myTimer() {
         var d = new Date();
         ws.send( d.toLocaleTimeString());
      }
      
      </script>
      
   </head>
   <body>
     
      <div id="sse">
         <a href="javascript:WebSocketSend()">send WebSocket</a>
      </div>
     
   </body>
</html>
Last edited by ThomasB on Wed Feb 01, 2017 5:39 pm, edited 7 times in total.

User avatar
rudi ;-)
Posts: 1134
Joined: Fri Nov 13, 2015 3:25 pm

Re: A very basic WebSocket Server

Postby rudi ;-) » Mon Jan 16, 2017 9:24 pm

ThomasB wrote:Howdy.

Strange...
..
If this doesn't work, I can create a small project and upload it, though this is very proprietary and hacky ;)


howdy :mrgreen:

the ws example works itself - nice work!


WS_demo.png
WS_demo.png (19.43 KiB) Viewed 6604 times


with your demo perhabs you build a small project for the github examples?
do you have a github ? would be fine!
i am sure - this is a usefull example!

why i asked that there perhabs things missing: ( have not seen a wifi Soft Ap setup so was building here for testing myself)

perhabs it would be good, if you insert the wifi ( Soft AP Mode ) things too
example:

// wifi init

Code: Select all

const static char *TAG = "WebSocketSvr";
// over menuconfig or set here ..
// #define WIFI_SSID "mySweetHome2017"
// #define WIFI_PASS "upsnotpuplichere"


static void wifi_conn_init(void)
{
    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK( esp_event_loop_init(wifi_event_handler, NULL) );
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = "mySweetHome2017", // or WIFI_SSID, 
            .password = "upsnotpuplichere", // or WIFI_PASS,
         .channel = 12,
         .max_connection = 4,
         .authmode = WIFI_AUTH_WPA_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) );
    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config) );
    ESP_LOGI(TAG, "start the WIFI SSID:[%s] password:[%s]\n", WIFI_SSID, WIFI_PASS);
    ESP_ERROR_CHECK( esp_wifi_start() );
}


and a small wifi_event_handler
https://github.com/espressif/esp-idf/bl ... sp_event.h

i like your example tom, it works!
thanks for share!

best wishes
rudi ;-)
-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

Re: A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 9:31 pm

Thats my WIFI init code, not sure if there is room for improvement, its copy/paste from somewhere:

Code: Select all

#include "WIFI.h"

#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"

#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event_loop.h"

static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;

wifi_config_t AP_config = {
   .ap = {
      .ssid = "WS_TEST",            /**< SSID of ESP32 soft-AP */
      .password = "none",         /**< Password of ESP32 soft-AP */
      .ssid_len=0,                  /**< Length of SSID. If softap_config.ssid_len==0, check the SSID until there is a termination character; otherwise, set the SSID length according to softap_config.ssid_len. */
      .channel=0,                     /**< Channel of ESP32 soft-AP */
      .authmode=WIFI_AUTH_OPEN,         /**< Auth mode of ESP32 soft-AP. Do not support AUTH_WEP in soft-AP mode */
      .ssid_hidden=0,                  /**< Broadcast SSID or not, default 0, broadcast the SSID */
      .max_connection=4,               /**< Max number of stations allowed to connect in, default 4, max 4 */
      .beacon_interval=100,            /**< Beacon interval, 100 ~ 60000 ms, default 100 ms */
   }
};

esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case SYSTEM_EVENT_STA_GOT_IP:
        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
        break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
        /* This is a workaround as ESP32 WiFi libs don't currently
           auto-reassociate. */
        esp_wifi_connect();
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
        break;
    default:
        break;
    }
    return ESP_OK;
}


void initialise_wifi(void){
    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_AP) );
    ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_AP, &AP_config) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}


I have github. I will have a look at it in the next days.

User avatar
rudi ;-)
Posts: 1134
Joined: Fri Nov 13, 2015 3:25 pm

Re: A very basic WebSocket Server

Postby rudi ;-) » Mon Jan 16, 2017 9:39 pm

ThomasB wrote:Thats my WIFI init code, not sure if there is room for improvement, its copy/paste from somewhere:
...
I have github. I will have a look at it in the next days.

..

this would be fine tom!
then make a pull request to examples,
i am sure this is a nice base with your html code too!

wifi, yes, know the cpy/pst code
https://github.com/espressif/esp-idf/bl ... ver.c#L210
.. perhabs a small change/append
STA -> AP

again, thanks for share,
your example : it works without an edit.

best wishes
rudi ;-)

( i will try to use it with android device too next few days )
Last edited by rudi ;-) on Mon Jan 16, 2017 9:42 pm, edited 1 time in total.
-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

Re: A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 9:41 pm

Gern geschehen ;)
(german: you are welcome)

User avatar
rudi ;-)
Posts: 1134
Joined: Fri Nov 13, 2015 3:25 pm

Re: A very basic WebSocket Server

Postby rudi ;-) » Mon Jan 16, 2017 9:44 pm

ThomasB wrote:Gern geschehen ;)
(german: you are welcome)

:mrgreen:
wie lautet dein github?
(english: how's your github called?)

best wishes
rudi ;-)
-------------------------------------
love it, change it or leave it.
-------------------------------------
問候飛出去的朋友遍全球魯迪

ThomasB
Posts: 31
Joined: Sun Dec 25, 2016 12:11 am

Re: A very basic WebSocket Server

Postby ThomasB » Mon Jan 16, 2017 9:47 pm

https://github.com/ThomasBarth but there is not too much going on. I post most of my work on http://barth-dev.de

Who is online

Users browsing this forum: No registered users and 3 guests