A very basic WebSocket Server

ThomasB
Posts: 38
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: 1729
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: 38
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: 38
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: 1729
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 32992 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: 38
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: 1729
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: 38
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: 1729
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: 38
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 15 guests