Giter Site home page Giter Site logo

theldus / wsserver Goto Github PK

View Code? Open in Web Editor NEW
400.0 22.0 76.0 2.16 MB

wsServer - a tiny WebSocket server library written in C

Home Page: https://theldus.github.io/wsServer

License: GNU General Public License v3.0

C 85.73% Makefile 6.94% CMake 2.35% Shell 3.41% Python 1.58%
websocket library c websocket-server websocket-library

wsserver's Introduction

wsServer

License: GPL v3 Build Status for Windows, Linux, and macOS

wsServer - a very tiny WebSocket server library written in C

Library

wsServer is a tiny, lightweight WebSocket server library written in C that intends to be easy to use, fast, hackable, and compliant to the RFC 6455.

The main features are:

  • Send/Receive Text and Binary messages
  • PING/PONG frames
  • Opening/Closing handshakes
  • Event based (onmessage, onopen, onclose)
  • Portability: Works fine on Windows, Linux (Android included), macOS and FreeBSD

See Autobahn report and the docs for an 'in-depth' analysis.

Building

wsServer only requires a C99-compatible compiler (such as GCC, Clang, TCC and others) and no external libraries.

Make

The preferred way to build wsServer on Linux environments:

git clone https://github.com/Theldus/wsServer
cd wsServer/
make

# Optionally, a user can also install wsServer into the system,
# either on default paths or by providing PREFIX or DESTDIR env
# vars to the Makefile.

make install # Or make install DESTDIR=/my/folder/

CMake

CMake enables the user to easily build wsServer in others environments other than Linux and also allows the use of an IDE to build the project automatically. If that's your case:

git clone https://github.com/Theldus/wsServer
cd wsServer/
mkdir build && cd build/
cmake ..
make
./examples/echo/echo # Waiting for incoming connections...

Windows support

Windows has native support via MinGW, toolchain setup and build steps are detailed here.

Keeping It Simple!

wsServer simplifies socket management by allowing you to focus on only three different sorts of events:

/* New client. */
void onopen(ws_cli_conn_t *client);

/* Client disconnected. */
void onclose(ws_cli_conn_t *client);

/* Client sent a text message. */
void onmessage(ws_cli_conn_t *client, const unsigned char *msg,
    uint64_t size, int type);

This is the only thing you need to worry about. You don’t have to think about return values in socket, accepting connections, or anything else. As a bonus, each client is handled in a separate thread, so there is no need to worry about that either.

A complete example

More examples, including their respective html files, can be found in examples/ folder, ;-).

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>

/**
 * @brief This function is called whenever a new connection is opened.
 * @param client Client connection.
 */
void onopen(ws_cli_conn_t *client)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("Connection opened, addr: %s\n", cli);
}

/**
 * @brief This function is called whenever a connection is closed.
 * @param client Client connection.
 */
void onclose(ws_cli_conn_t *client)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("Connection closed, addr: %s\n", cli);
}

/**
 * @brief Message events goes here.
 * @param client Client connection.
 * @param msg    Message content.
 * @param size   Message size.
 * @param type   Message type.
 */
void onmessage(ws_cli_conn_t *client,
    const unsigned char *msg, uint64_t size, int type)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("I receive a message: %s (%zu), from: %s\n", msg,
        size, cli);

    sleep(2);
    ws_sendframe_txt(client, "hello");
    sleep(2);
    ws_sendframe_txt(client, "world");
}

int main(void)
{
    /*
     * Main loop, this function never* returns.
     *
     * *If the .thread_loop is != 0, a new thread is created
     * to handle new connections and ws_socket() becomes
     * non-blocking.
     */
    ws_socket(&(struct ws_server){
        /*
         * Bind host, such as:
         * localhost -> localhost/127.0.0.1
         * 0.0.0.0   -> global IPv4
         * ::        -> global IPv4+IPv6 (Dual stack)
         */
        .host = "localhost",
        .port = 8080,
        .thread_loop   = 0,
        .timeout_ms    = 1000,
        .evs.onopen    = &onopen,
        .evs.onclose   = &onclose,
        .evs.onmessage = &onmessage
    });

    return (0);
}

the example above can be built with: make examples.

WebSocket client: ToyWS

Inside extra/toyws there is a companion project called ToyWS. ToyWS is a very simple & dumb WebSocket client made exclusively to work with wsServer. Extremely limited, its usage is highly discouraged with other servers other than wsServer and is only meant to be used in conjunction with wsServer.

This mini-project only serves as an aid to wsServer and frees the user from using additional projects to use wsServer in its entirety.

More info at: extra/toyws/README.md

SSL/TLS Support

wsServer does not currently support encryption. However, it is possible to use it in conjunction with Stunnel, a proxy that adds TLS support to existing projects. Just follow these four easy steps to get TLS support on wsServer.

Contributing

wsServer is always open to the community and willing to accept contributions, whether with issues, documentation, testing, new features, bugfixes, typos... welcome aboard. Make sure to read the coding-style guidelines before sending a PR.

Was the project helpful to you? Have something to say about it? Leave your comments here.

License and Authors

wsServer is licensed under GPLv3 License. Written by Davidson Francis and others contributors.

wsserver's People

Contributors

cleberpeter avatar ejoerns avatar gloveboxes avatar hoathienvu8x avatar jfdelnero avatar refutationalist avatar roddehugo avatar silvioprog avatar terziev-viktor avatar theldus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wsserver's Issues

mutiple sockets not working.

Description

I have a small utility serving up 3 sockets on 6060, 6061 and 6062.

I have previously compiled 3 independent files, each serving up one of the 3 sockets and this appears to work perfectly but if I combine the code into a single file and compile it, all of the responses are served up by a single message receiver.

Your environment

Both on Linux 22 and stm32embedded
- Have you tested under the echo/echo.html example? yes/no

my C code

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include "../include/ws.h"

#include "data.h"
#include "graph.h"
#include "alerts.h"

#define MAX_PORTS   9

/**
 * @dir examples/
 * @brief wsServer examples folder
 */

/*
 * @dir examples/echo
 * @brief Echo example directory.
 * @file echo.c
 * @brief Simple echo example.
 */

/**
 * @brief Called when a client connects to the server.
 *
 * @param client Client connection. The @p client parameter is used
 * in order to send messages and retrieve informations about the
 * client.
 */
void onDataOpen(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
	printf("Data Connection opened, addr: %s\n", cli);
#endif
}

void onGraphOpen(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
	printf("Graph Connection opened, addr: %s\n", cli);
#endif
}

void onAlertsOpen(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
  #ifndef DISABLE_VERBOSE
	printf("Alerts Connection opened, addr: %s\n", cli);
  #endif
}

void onDataClose(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
	printf("Data Connection closed, addr: %s\n", cli);
#endif
}

void onGraphClose(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
	printf("Graph Connection closed, addr: %s\n", cli);
#endif
}

void onAlertsClose(ws_cli_conn_t *client)
{
	char *cli;
	cli = ws_getaddress(client);
#ifndef DISABLE_VERBOSE
	printf("Alerts Connection closed, addr: %s\n", cli);
#endif
}


void onDataMessage(ws_cli_conn_t *client,	const unsigned char *msg, uint64_t size, int type) {
	char *cli;
	cli = ws_getaddress(client);
	
  #ifndef DISABLE_VERBOSE
	printf("Received a Data message: %s (size: %" PRId64 ", type: %d), from: %s\n", msg, size, type, cli);
  #endif
	 ws_sendframe(NULL, &dvalues[0], strlen(dvalues), type);
	 }
	 

void onGraphMessage(ws_cli_conn_t *client,	const unsigned char *msg, uint64_t size, int type) {
	 char *cli;
	 cli = ws_getaddress(client);	 
	 
	 #ifndef DISABLE_VERBOSE
	 printf("Received a Graph message: %s (size: %" PRId64 ", type: %d), from: %s\n", msg, size, type, cli);
	 #endif

	 ws_sendframe(NULL, &gvalues[0], strlen(gvalues), type);
	 }
	 	 

void onAlertMessage(ws_cli_conn_t *client,	const unsigned char *msg, uint64_t size, int type) {
	 char *cli;
	 cli = ws_getaddress(client);
	 #ifndef DISABLE_VERBOSE
	 printf("Received an Alert message: %s (size: %" PRId64 ", type: %d), from: %s\n", msg, size, type, cli);
   #endif

	 ws_sendframe(NULL, &avalues[0], strlen(avalues), type);
	 }
	 

/**
 * @brief Main routine.
 *
 * @note After invoking @ref ws_socket, this routine never returns,
 * unless if invoked from a different thread.
 */
int main(void)
{

  ws_socket(&(struct ws_server){
    .host					 = "0.0.0.0",
    .port 				 = 6060,
    .thread_loop   = 1,
    .timeout_ms    = 10000,
		.evs.onopen    = &onDataOpen,
		.evs.onclose   = &onDataClose,
		.evs.onmessage = &onDataMessage
		});  
	
  ws_socket(&(struct ws_server){
    .host					 = "0.0.0.0",
    .port 				 = 6061,
    .thread_loop   = 1,
    .timeout_ms    = 10000,
 		.evs.onopen    = &onGraphOpen,
		.evs.onclose   = &onGraphClose,
		.evs.onmessage = &onGraphMessage
		});    
		
		
  ws_socket(&(struct ws_server){
    .host				 	 = "0.0.0.0",
    .port        	 = 6062,
    .thread_loop 	 = 1,
    .timeout_ms  	 = 10000,
		.evs.onopen    = &onAlertsOpen,
		.evs.onclose   = &onAlertsClose,
		.evs.onmessage = &onAlertMessage
		});		
	
	while(1){
		 int i = 0;
		 i++;
		 if (i>10000) {
		 	 i = 0;
		 }
	};

	/*
	 * If you want to execute code past ws_socket, invoke it as follows:
	 *   ws_socket(&evs, 6060, 1, 1000);
	 */

	return (0);
}

Three sockets are opened on 6060,6061 and 6062,

All callbacks are serviced by the AlertMessage callbacks. ( OnAlertsOpen, OnAlertsClose and onAlertMessage )

Maybe not only doing a server?

Hi

I want to using this library to doing a client.
I need to change code or not?

I think maybe need change code can do it, but maybe not?

thank you

Server processing time increase at each new client connection

Hi,
I have an unattended behaviour in the next context :

I have got a websocket wsserver server ready for connections.

The client that connects is always doing the same work :
it sends an init string,
then reads all the buffer sent by the server (almost 2megabytes data sent by binary chunks/ and some texts messages )
and closes.

I measured the processing time on client side to measure the whole process.

The wsServer is really fast with synchronous . (The best in comparison to websocketpp or Qt websocket)

But , each time a new client is launched and the server has done the job, the processing time to receive all the data increases:

eg here is client processing time sample increasing:
2.033s
2.07s
2.337s
2.649s
3.158s
3.607s
3.739s
4.319s....

I don't really understand, since there is always only one client connected at a time.

Have you got some idea ?

Optional forked server application model

Hi @Theldus,

very nice looking library, thank you for making it available. 🥇

I'm in the course of evaluating libs and your approach with utter simplicity, but all the robustness is very convincing.

I'm looking into making the individual Websocket server workers into separate processes instead of threads, for various reasons, including robustness, security and (future) impersonation i.e. set_uid(). (Windows would be left out, as it does not support fork()).

The forked server model is described here (in my case it would not go through inetd but through a reverse proxy):
https://www.ibm.com/docs/en/zos/3.1.0?topic=considerations-forked-server-application-model

Do you think wsServer would be a fit to implement the forked pattern?

I haven't yet studied the source much, but the following would have to be done (I guess):

  1. fork() instead of creating a thread.
  2. In the parent process, perhaps store the child pid in the client list, for some signalling.
  3. In the parent process, close the websocket file handle, but don't tear it down.
  4. In the child process, close all the other socket file handles, again without tearing anything down.
  5. In the child process, stop the accept loop of the server, and prevent its tear-down when the child ends.
  6. In the child process, handle just the one websocket until it closed, i.e. do the actual work, as you would in a thread.

I can always do this in a copy of the project, of course, but obviously, an integration using some #ifdefs would be better.

Any chance of you accepting a PR later? 😎

_Mark

re-send to all clients ?

Hi, I play around with your lib and see, that I can resend the received message to sender only

void onmessage(int fd, const unsigned char *msg, uint64_t size, int type) {
	char *cli;
	cli = ws_getaddress(fd);
	printf("I receive a message: %s (size: %" PRId64 ", type: %d), from: %s/%d\n", msg, size, type, cli, fd);
	free(cli);

	ws_sendframe(fd, (char *)msg, size, true, type);
}

How to send a message to all (or special) connected clients ?
Maybe my question is to general, but I'm a C rooky and maybe there is already such a list of clients ?

ws_getaddress() do not work in onmessage() on localhost

When using the wsServer on localhost the IP can be retrieved in open and close, but not in onmessage():

Connection opened, addr: 127.0.0.1
I receive a message: {"data": ... } (451), from: (null) <-------- PROBLEM HERE
Connection closed, addr: 127.0.0.1

The code used in this test is based on the demo example:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>

/**
 * @brief This function is called whenever a new connection is opened.
 * @param client Client connection.
 */
void onopen(ws_cli_conn_t *client)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("Connection opened, addr: %s\n", cli);
}

/**
 * @brief This function is called whenever a connection is closed.
 * @param client Client connection.
 */
void onclose(ws_cli_conn_t *client)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("Connection closed, addr: %s\n", cli);
}

/**
 * @brief Message events goes here.
 * @param client Client connection.
 * @param msg    Message content.
 * @param size   Message size.
 * @param type   Message type.
 */
void onmessage(ws_cli_conn_t *client,
    const unsigned char *msg, uint64_t size, int type)
{
    char *cli;
    cli = ws_getaddress(client);
    printf("I receive a message: %s (%zu), from: %s\n", msg, size, cli);

    ws_sendframe_txt(client, "OK");
}

int main(void)
{
    /* Register events. */
    struct ws_events evs;
    evs.onopen    = &onopen;
    evs.onclose   = &onclose;
    evs.onmessage = &onmessage;

    /*
     * Main loop, this function never* returns.
     *
     * *If the third argument is != 0, a new thread is created
     * to handle new connections.
     */
    ws_socket(&evs, 8181, 0, 1000);

    return (0);
}

I had a blast with your wsServer library the last few days.

I spend my entire career trying to avoid C so despite decades in IT I only spend a week so far on learning C.

For a project I need to send messages from a browser to a raspberry pi zero w. In turn it drives TMC2209 stepper controllers etc etc.
Your library is indeed a breeze to start with and I have it working nicely receiving binary data.

Now I wonder, is it possible to have an animation loop running while wsServer is doing it's thing?

ws_socket(&evs, 8080);

Hogs the main loop and I can't figure out how I can keep my own code going while we wait for the next message.
Maybe a hook for a callback or a function call so I can push the wsServer loop along manually.
Maybe there is a solution already but after seeing the thread stuff I decided to grease up and see if you have any ideas.

Cheers.

Paul.

Bind to http server

This is is an awesome work, setting up and running is a breeze, it just does the work, great library sir.

Please what is the best way of binding this to an http server to also serve html static files, looking at designing a flow similar to express and ws in nodejs. Thanks for such a great work

CentOS compile error

I'm trying to compile wsServer on a somewhat dated server running CentOS and am getting this error when using make:

/examples/echo/../..//libws.a(ws.o): In function `close_timeout': 
ws.c:(.text+0x5bf): undefined reference to `clock_gettime'

My research shows that older glibc versions need the -lrt flag for linking but I have to admit that I'm not too good with Makefiles and I wasn't quite sure where it should go in there. Any hints would be appreciated, but it really isn't that important.

Here is some info on the machine:

$ ldd --version
ldd (GNU libc) 2.12

$ uname -a
Linux redacted 2.6.32-754.35.1.el6.x86_64 #1 SMP Sat Nov 7 12:42:14 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/system-release
CentOS release 6.10 (Final)

Creating variable "send" segfaults

Using the example program if a variable is created with name "send" it will print "Waiting for incoming connections..." and once a client attempts to connect it will segfault with no other error

<feature> (Maybe) Send PING frames to identify inactive connections

Description

Devices that are able to sleep or situations where there is an abrupt drop in connection (such as a weak WiFi signal) can cause the socket to not close immediately, even if the device is unable to receive messages from the server.

Situations like this make wsServer keep these devices in the clients list, even if they are virtually disconnected from the server, this causes:

  • ws_sendframe* try to send messages to them during broadcasts
  • send() to them returns as if the message had been sent
  • occupy limited slots of the client list and also other server resources

In addition to being inconvenient, this can easily run out of resources if a malicious user launches a Denial-of-Service attack.

In order to prevent this, wsServer can (optionally) support sending periodic PINGs frames with defined interval: if in <amount of time> there is no PONG response, abort the connection.

Since this belongs to the WebSockets specification, all clients are expected to support PING frames and respond to them, so there are no worries in that regard.

Implementation

Although it sounds simple, there are many ways to implement it.

My idea would be to unify the close and PING timeout in one place: a 'timeout thread', which would handle 'timeout events'.

The function of this thread would be to periodically wake up and check which pending 'events' it needs to handle: if a close timeout, check if the close timeout has elapsed and take the appropriate actions, if PING timeout, check if the timeout has elapsed and also take the appropriate actions.

This would greatly simplify the close process (since the current approach creates N timeout threads, one per client), support PING timeout, and pave the way for supporting a proper wsServer shutdown (issue #31).


Note 1: As this feature makes use of threads and etc, I'm planning on making it optional, enabled at compile time.
Note 2: I'm marking it as 'help wanted' because it's a more or less complicated to implement nicely, any help is welcome.

Error: A server must not mask any frames that it sends to the client.

Hi - first, I would like to thank you for this awesome library! It's very well written and easy to modify (I ported it to Zephyr RTOS platform for my project).

I encountered an issue where the client (Chrome web app) reports: A server must not mask any frames that it sends to the client. My server side logic sends multiple 64KB frames using ws_sendframe_bin().

It puzzles me because that issue seem to be data dependent - I tried with an empty byte array and it works fine. How could I debug this further?

Connection UID

Hi, great code! I wonder if is it possible to make a uid for each connected client for database purpose like websocket-sharp for C#.
is is possible? If not can you consider it as a TODO?

Also having a OnError event would be nice.

<feature> Ability to store context (user data) for later retrieval in callbacks

Working with wsServer today, I ran into one issue. The code inside of the callback functions (onopen(), onclose(), etc) does not have any context other than the client connection that is being passed in. This makes it a bit hard to tie the code in with whatever goes on in the surrounding program.

For example, let's say I wanted to maintain a list of connected clients. I could whip up some struct/array to do that. Now, in onopen(), I would want to add the newly connected client to my collection. But how do I access it from this function? The only solution seems to be a global variable, which isn't exactly great.

If we could hand in a pointer to user data upon calling ws_socket(), then the callbacks could include this as the last parameter. Like so:

int ws_socket(struct ws_events *evs, uint16_t port, int thread_loop, void *context);
void onopen(ws_cli_conn_t *client, void *context);

Alternatively, two functions could be introduced. One to set/ save a context, the other one to get/ retrieve it. This way, the current interface would remain the same, keeping backwards compatibility.

Or is there some other way you can already do this without a global variable in the user code?

wsserver via ssl in linux environment

Situation description

I am running Debian 11 on a small Linux board on the local network whre also my PC is present.
On this board a C-application is running which reads all hardware. Within this C-application an instance of wsServer is running. Also on the board a http: webserver is running. When a website is opened on the board, it starts a ws-client to wsServer and the status of the hardware is communicated to the website via the websocket. This way the hardware status is shown in real-time on the webpage. Beautiful!

Item nr 1

The website can only open a websocket connection to the board when using the hostname or the IP address of the board.
When using localhost or 127.0.0.1 a connection is not created App.vue:104 WebSocket connection to 'ws://localhost:8080/' failed:.
So far so good, normally this would not be a big issue.
But.

Item nr 2

When the website is accessed from outside the local network via a router we have seen that the console of the browser window is showing a mixed content error Mixed Content: The page at xxx was loaded over HTTPS, but requested an insecure yyy. This is correct because in that situation the website is called via https: and the websocket connection would be opened via ws:

SSL support

I have read your instructions for enabling SSL, but I can't get it to work. In no situation a wss: connection is opened WebSocket connection to 'wss://10.7.52.120/' failed:. I tested with localhost, 127.0.0.1 and the board's IP address, none of them work.

I also tried a few other options for the stunnel config like the following, but also no positive result:

[wsServer]
cert = /etc/stunnel/server.pem
accept = 0.0.0.0:443
connect = localhost:8080

or

[wsServer]
cert = /etc/stunnel/server.pem
accept = 0.0.0.0:443
connect = <board ip-addres>:8080

even

[wsServer]
cert = /etc/stunnel/server.pem
accept = 0.0.0.0:443
connect = 8080

Could you help me with this please?
Looking forward to hearing from you

Best regards,
Raymond

runtime error for left shifts with fsanitize=address,undefined in sha1.c

Hi and thank you for a very good websocket library in C.

I not really sure if this is legit as I Am not very skilled with C.

Anyway, got this when running an executable compiled with sanitize enabled:
wsServer/sha1.c:250:46: runtime error: left shift of 128 by 24 places cannot be represented in type 'int'

My research suggests it may be fixed by doing a cast to uint32_t before shifting in SHA1ProcessMessageBlock function

Orignal:

    {   
        W[t] = context->Message_Block[t * 4] << 24; 
        W[t] |= context->Message_Block[t * 4 + 1] << 16; 
        W[t] |= context->Message_Block[t * 4 + 2] << 8;
        W[t] |= context->Message_Block[t * 4 + 3]; 
    }   

New:

    {   
        W[t] = (uint32_t)context->Message_Block[t * 4] << 24; 
        W[t] |= (uint32_t)context->Message_Block[t * 4 + 1] << 16; 
        W[t] |= (uint32_t)context->Message_Block[t * 4 + 2] << 8;
        W[t] |= (uint32_t)context->Message_Block[t * 4 + 3]; 
    }   

But I don't know if will cause issues in some other parts of the library or whatever?

How can I create a list of connected clients using the wsServer library? (question)

Greetings,

I am currently working with the wsServer library and I am trying to achieve two tasks. First, I need to create a list of connected clients. Second, I want to broadcast a message to some of the clients on the list based on their file descriptor (FD).

I attempted to create an array of connected clients using the syntax ws_cli_cont clients[size]. However, I received an error message indicating that ws_cli_cont is not a complete type. I am not sure if this is the correct way to create an array of connected clients.

Additionally, I need to differentiate between clients based on their FD to broadcast a message only to a specific group of clients. Can someone guide me on the proper way to create a list of connected clients and broadcast a message based on FD using the wsServer library? Any advice or code examples would be greatly appreciated.

Thank you in advance.

Clients do not receive send frames

Hi,
I use this great library in a small project of mine where I want to send a status message when a new client connects.
I first tried using ws_sendframe_txt() in the onopen() function to broadcast to all clients (using NULL as the client parameter) which did not work. No error was thrown as far as I could see but the clients didn't receive the send frame.
Using the provided client parameter however worked as expected.

Dart websocket clinet

hi,
dart websocket client connect error
HttpException: Connection closed before full header was received

suggest for some conditions.

In function ws_accept():

/* Accept. */
new_sock = accept(sock, (struct sockaddr *)&client, (socklen_t *)&len);

if (timeout)
{
    time.tv_sec = timeout / 1000;
    time.tv_usec = (timeout % 1000) * 1000;

    /*
     * Socket timeout
     * This feature seems to be supported on Linux, Windows,
     * macOS and FreeBSD.
     *
     * See:
     *   https://linux.die.net/man/3/setsockopt
     */
    setsockopt(new_sock, SOL_SOCKET, SO_SNDTIMEO, &time,
        sizeof(struct timeval));
}

if (new_sock < 0)
    panic("Error on accepting connections..");

condition if (new_sock < 0) may move up before if (timeout) ?

<feature> How to gracefully shutdown the server?

Hi.
I want do something that when the wsServer get "exit" message from client,it will exit normal. because when i use valgrind to check my code,it always show memory leak.if wsServer can exit,i don't need to press Ctrl +C

Sorry! my English is terrible. I used google translation. I hope my description is clear.

send_all function return value error

Hi there.
SEND macro returns 0 instead of sent bytes.
I searched the ws.c and found that send_all function returns 0.
I think send_all must return the sent bytes.

Only connect and send ? [client only]

There are great examples how to run a server with ease, but unfortunately I'm a bloody C beginner.

How to make a C program with your lib, which only connect to server and send some text ?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ws.h>

int main(void)
{
     while (1) {
	 ws_sendframe_txt(fd, "hello", false); // but from where get this 'fd' ?
         delay(100);
    }
}

But how to connect to server and from where I get this fd ?

Server closes the socket after a time. How do I ensure it stays open?

Description

The server socket closes after a time ( varies )

I'm not sure if it's the browser/javascript end that is abandoning the connection or the server itself that's closing it .......

My web page tries to send a query to my socket and I get a 'WebSocket is already in CLOSING o CLOSED state' console message on the browser console.

(C question) Pd external and macOS

Hi,

Thanks for this project. :)

I'm building a Pure-Data external that holds wsServer. So far it works fine on Linux and Windows but I'm having a strange issue on macOS: as soon as a client connects I get a segfault.

I'm scratching my head. I came around this issue Lucarda/pd-websocketserver#1 some time ago. I'm now updating the wsServer sources but get the same debugger (lldb) message: pointing to base64_encode.

I'm working on this branch: https://github.com/Lucarda/pd-websocketserver/tree/v0.0.2

I'm on kindergarten/primary on C.

Could it be some compiler flag? These are defined https://github.com/Lucarda/pd-websocketserver/blob/v0.0.2/pd-lib-builder/Makefile.pdlibbuilder#L506-L536

The main makefile is: https://github.com/Lucarda/pd-websocketserver/blob/v0.0.2/Makefile

Any clues?


I'm compiling and testing with:

msys2/MinGW on Windows10
gcc on Debian10
clang on (QEMU virtual machine / Catalina macOS) NOTE: the wsServer/example/send_receive.c works fine.

ws

add an #include

thank you for this very (!) nice WebSocket program.
It is far the best I found in the web after days of searching, because its simple and does what I need.
To make it more compatible to some compilers:
#include <stdint.h>
should be added to ws.c

Is it easy to modify the receive / send functions for binary data ?

ws.h

All is well of course. But where is the file ws.h? )))

Compile on Windows?

Hi! I tried to compile the library on Windows by changing sys/socket.h and arpa/inet.h includes to winsock2.h and ws2tcpip.h, but failed. Can you help with them? Thank You!

libws.a(ws.c.obj):ws.c:(.text+0x22): undefined reference to `getpeername@12'
libws.a(ws.c.obj):ws.c:(.text+0x4a): undefined reference to `inet_ntoa@4'
libws.a(ws.c.obj):ws.c:(.text+0x5c9): undefined reference to `socket@12'
libws.a(ws.c.obj):ws.c:(.text+0x61e): undefined reference to `setsockopt@20'
libws.a(ws.c.obj):ws.c:(.text+0x656): undefined reference to `htons@4'
libws.a(ws.c.obj):ws.c:(.text+0x677): undefined reference to `bind@12'
libws.a(ws.c.obj):ws.c:(.text+0x6a9): undefined reference to `listen@8'
libws.a(ws.c.obj):ws.c:(.text+0x6f4): undefined reference to `accept@12'
collect2.exe: error: ld returned 1 exit status
CMakeFiles\send_receive.dir\build.make:105: recipe for target 'send_receive.exe' failed
mingw32-make[2]: *** [send_receive.exe] Error 1
CMakeFiles\Makefile2:122: recipe for target 'CMakeFiles/send_receive.dir/all' failed
mingw32-make[1]: *** [CMakeFiles/send_receive.dir/all] Error 2
Makefile:101: recipe for target 'all' failed
mingw32-make: *** [all] Error 2

Use wsServer code as a webscoket client

Hello:

I use wsServer successfully in my project. I need a websocket client written in C.
How would I use your existing code to implement a simple websocket client? Is it possible? Any pointers?

thank you,

west suhanic

<feature> Accessing to connection headers

Hello,

It will be a good option to have access to the header parameters (X-Real_IP, X-....) of the handshaking part to get access to a other data like client IP or an ID in the case of proxy forwarding (NGINX) because later with the ws_getaddress function only get the server IP.

Thanks.

Multiple replies.

Hello,

If I open the sample .HTML in two or more tabs, connect all instances and send anything, I retrieve a number of "recv" as tabs opened. In the server I see that the tabs are well identified by different FD's.

Problem receiving binary messages

The binary message sent is:0x1a,0x2b,0x3c,0x4d,0x00,0x5c,0x6d,0xdd,0xaa,0xac ,But received as :0x1a ,0x2b ,0x3c ,0x4d ,0x3f ,0xe5 ,0x32 ,0xa2 ,0x95 ,0x15
If it does not contain 00 bytes when sending, the received message is correct, could you give me some suggestions ,thanks .

Stress testing send_receive.c fails

I stress tested the send_receive example with artillery.io

--------------------------------------
Metrics for period to: 10:55:20(-0300) (width: 9.004s)
--------------------------------------

vusers.created_by_name.0: ................................... 80
vusers.created.total: ....................................... 80
vusers.failed: .............................................. 24
vusers.completed: ........................................... 56
vusers.session_length:
  min: ...................................................... 1.2
  max: ...................................................... 12.4
  median: ................................................... 5.9
  p95: ...................................................... 10.3
  p99: ...................................................... 10.9
websocket.send_rate: ........................................ 29/sec
websocket.messages_sent: .................................... 224
errors.Parse Error: Expected HTTP/: ......................... 24

I have tried to increase the amount of clients and ports and recompiling the example, however it still fails.

Do I need to augment file descriptors on my system in order to allow many clients sending and receiving at the same time?

PS: I like the interface of this library it's very intuitive, great work!

Only works on a loopback adapter

I'm trying to use your wsServer with a small RPi application, but I'm having an issue that the server is only available on the 127.0.0.1 address...

How can I bind it to the eth0 interface (instead of the loopback adapter)?

Error building/compiling on mac silicon.

I am trying to use the wsServer library in my project, but I am encountering the following linker error every time I try to compile my code:

Undefined symbols for architecture arm64:
  "_ws_getaddress", referenced from:
      _onopen in echo-65824f.o
      _onclose in echo-65824f.o
      _onmessage in echo-65824f.o
  "_ws_sendframe", referenced from:
      _onmessage in echo-65824f.o
  "_ws_socket", referenced from:
      _main in echo-65824f.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Although the library builds successfully, it fails to generate the example executables. When I try to compile the examples manually with the following command:
clang echo.c -I/Users/Mark/Documents/UnivProject/src/wsServer/include

I receive the same linker error. I would appreciate any help or guidance on how to resolve this issue and use the library successfully in my project. Thank you!

Static Library undefined references on Windows

Hi, I am a web developer and this is my first time diving into C projects, but I couldn't find any resolution to this problem after some good googleing so I thought maybe someone in this community has dealt with similar issues.

Here is my CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(server C)

set(CMAKE_C_STANDARD 99)

include_directories(include)

add_executable(server
        src/server.c)
target_link_libraries(server ${CMAKE_SOURCE_DIR}/src/wsServer/build/libws.a)

And here is the undefined resources errors:

====================[ Build | server | Debug ]==================================
"C:\Program Files\JetBrains\CLion 2021.3.4\bin\cmake\win\bin\cmake.exe" --build C:\INODev\age-of-io\c\cmake-build-debug --target server
[1/1] Linking C executable server.exe
FAILED: server.exe 
cmd.exe /C "cd . && C:\PROGRA~1\JETBRA~1\CLION2~1.4\bin\mingw\bin\gcc.exe -g  CMakeFiles/server.dir/src/server.c.obj -o server.exe -Wl,--out-implib,libserver.dll.a -Wl,--major-image-version,0,--minor-image-version,0  ../src/wsServer/build/libws.a  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x13): undefined reference to `__imp_accept'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0xcd): undefined reference to `__imp_closesocket'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x4d5): undefined reference to `__imp_closesocket'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x6b5): undefined reference to `__imp_send'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x8fd): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x935): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x975): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x9b5): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x9f5): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0xaeb): more undefined references to `__imp_recv' follow
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x10ad): undefined reference to `__imp_getpeername'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x10dc): undefined reference to `__imp_inet_ntop'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x148d): undefined reference to `__imp_send'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x178a): undefined reference to `__imp_recv'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x1979): undefined reference to `__imp_closesocket'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x1aaf): undefined reference to `__imp_send'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x28a4): undefined reference to `__imp_WSAStartup'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x28dd): undefined reference to `__imp_socket'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x2912): undefined reference to `__imp_setsockopt'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x2935): undefined reference to `__imp_htons'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x294f): undefined reference to `__imp_bind'
C:\Program Files\JetBrains\CLion 2021.3.4\bin\mingw\bin/ld.exe: ../src/wsServer/build/libws.a(ws.c.obj):ws.c:(.text+0x2966): undefined reference to `__imp_listen'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

License Change Consent - wsServer Project

License change proposal

Hello wsServer contributors,
@silvioprog,
@gloveboxes,
@CleberPeter,
@jfdelnero,
@refutationalist,
@ejoerns,
@hoathienvu8x,
@terziev-viktor,
@roddehugo

I am writing to you to propose a change of license for the wsServer project, from the current GNU General Public License v3.0 (GPLv3) to the MIT License (or equivalent). I am considering this change to make wsServer more inclusive and easier to use by other people.

The MIT License differs from the GPLv3 in the following aspects:

  • The MIT License is a permissive license, which means that it allows anyone to use, modify, distribute, or sublicense the software without any restrictions.
  • The GPLv3 is a copyleft license, which means that it requires anyone who uses, modifies, distributes, or sublicenses the software to also release their work under the same license.
  • The MIT License only requires that the original license and copyright notice be included in any copies or substantial portions of the software.

The change of license would only take place if all the contributors agree to it. Therefore, I kindly ask you to reply to this message with your acknowledgment or objection to the proposed change.

Personally, I do not mind the current license, but I think a more permissive one can help the project reach new heights. However, you do not have to feel pressured to change the license and any opinion is welcome.

You can consult the full text of the two licenses here: MIT License and GNU General Public License v3.0.

Thank you for your attention and your contributions to wsServer.

Sincerely,
Davidson Francis (@Theldus)

Ping - pthread_mutex_lock called on a destroyed mutex

I am not a C/C++ maestro in any way and I've been fighting with adding ping support to my app.

My program has a main loop that broadcasts a message to all connected clients 30 times per second. This works fine.

Now I need to remove inactive clients as soon as possible(pings)

What I tried:

  • Adding a call to ws_ping every 30 loop iterations
  • Creating a thread with pthread_create. It has a loop that calls ws_ping every second(similar to the ping example from this repository)

With both approaches I'm getting this error right after launching the program:
FORTIFY: pthread_mutex_lock called on a destroyed mutex (0x7817969390)

I'm running this on Android (https://github.com/tesla-android/android-external-libws), but this shouldn't change anything -> I just provided a custom makefile for AOSP.

The program I want to run with wsServer is opensource https://github.com/tesla-android/android-external-tesla-android-virtual-display/blob/main/tesla-android-virtual-display.cpp

I just replaced the mjpeg streamer with wsServer: https://pastebin.com/cKZnXu1n

I would very much appreciate any suggestions on how to get rid of this problem!

Thank you for creating this awesome library @Theldus, it's very easy to use and well documented!

Problem with recent change to the calling sequence for the function 'ws_socket()

The recent change to the calling sequence for the function 'ws_socket(struct ws_server *ws_srv)' causes problems if the 'ws_server' structure is in the stack of the calling function and the function exits after calling a non-blocking 'ws_socket()'.

Previously, the callers 'ws_events' structure was copied the wsServer's 'ws_server' structure. Now, the caller's pointer to it's 'ws_server' structure is placed in the wsServer's 'ws_accept_params' structure.

When the calling function exits, it's 'ws_server' structure becomes undefined. The solution for the caller is to allocate the 'ws_server' structure in local space.

A note of this should be included in the documentation. And maybe the examples like 'echo' should do this instead of using stack allocation.

My environment:

  • wsServer version (commit hash)
  • OS version: Debian GNU/Linux 12 (bookworm)
    • If Linux:
      • kernel version: 6.1.0-15-amd64
      • Libc used: Debian GLIBC 2.36-9+deb12u3
  • Architecture: x86-64...

Close Event Not Always Firing on Server

Hi @Theldus, I made a tiny game server using your amazing library, I did entire development on localhost and yesterday I got a VPS to run it on real server.

A weird bug happens when I'm using wsServer on VPS, when clients disconnect from server it doesn't always fire onclose it happens randomly like 1 in 5, I have no idea why is this happening and why it only happens on VPS and not localhost.

Do you have any idea how can I fix it? I toke a look at source code it seems neat and fine but still it happens.

I'm using Windows Server 2019

Compile error with 32-bit system

Hi,
i was trying to use your library in a project at my university.
It runs fine and is easy to use and compile on my local system but my code has to run on a 32-bit ARM processor.

I get the following error during compilation:

.../wsserver-src/src/ws.c: In function ‘read_frame’:
.../wsserver-src/src/ws.c:813:30: error: left shift count >= width of type [-Werror=shift-count-overflow]
    (((size_t)next_byte(wfd)) << 56) | /* frame[2]. */
                              ^~
.../wsserver-src/src/ws.c:814:30: error: left shift count >= width of type [-Werror=shift-count-overflow]
    (((size_t)next_byte(wfd)) << 48) | /* frame[3]. */
                              ^~
.../wsserver-src/src/ws.c:815:30: error: left shift count >= width of type [-Werror=shift-count-overflow]
    (((size_t)next_byte(wfd)) << 40) | (((size_t)next_byte(wfd)) << 32) |
                              ^~
.../wsserver-src/src/ws.c:815:65: error: left shift count >= width of type [-Werror=shift-count-overflow]
    (((size_t)next_byte(wfd)) << 40) | (((size_t)next_byte(wfd)) << 32) |
                                                                 ^~

You seem to assume that size_t is 64-bits wide. That leads to issus for 32-bit systems.
Would it be possible to replace the use of size_t with uint64_t here? Or do you have any other idea how to fix this?

Problems with browsers connections

Hi,
I'm embedding the library in my GTK/C application on Linaro machine.
I need to connect the application (server) with a web page (client) - so by the javascript WebSocket object.

On the C side I started the server with:

ws_socket(&evs, 8080, 1, 1000);
cause I need other C routines run.
On the browser side I try to connect with:
var socket = new WebSocket("ws://localhost:8080");

As reply I always receive the error:
WebSocket connection to 'ws://localhost:8080/' failed:

On the C side I see the output in console Waiting for incoming connections... and no other.
If I check the opened port by 'netstat -a ' I don't see the waiting 8080 port.

Any Idea?
Thank a lot!

ADDED: by the 'netstat -ap --numeric-ports' I see the following output:
# netstat -ap | grep rech
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 10700/lockrecharge
unix 3 [ ] STREAM CONNECTED 1571165 10700/lockrecharge
unix 3 [ ] STREAM CONNECTED 1577223 10700/lockrecharge
`

So the socket is listening!

<feature> Lower-level, non event-based APIs too

Description

In issue #24, was discussed the possibility of having a low-level API for finer control over wsServer.

This is a much-desired feature: while the current API provides a clean and intuitive way on how to use web sockets, it hides many implementation details, such as the dynamic memory allocations that occur under the hood.

A lower-level, non-event-based approach becomes interesting for using wsServer in embedded systems, where memory usage and OS resources are scarce.

Additional routines like the ones described below might be interesting in this scenario:

  • ws_listen: create the socket and let the server listen on a port

  • ws_accept: equivalent to accept. Accepts a single connection and performs the handshake. From this point on, it becomes possible to send and receive messages.

  • ws_receiveframe: Receives a single frame (control frame or not) and saves it in a buffer provided by the user, does not perform dynamic memory allocations. CONT-type frames require multiple invocations to this routine. Return parameters and variables to inform frame content, type, error, and success conditions are important.

(I leave the parameters and return of the functions open, as this can change at any time).

It is also important to leave the management of client lists, pthreads, locks, etc, to the event-based implementation (or not, I'm still thinking about it). The idea is that this API becomes the foundation of the current API: which is higher-level and event-based.

PS: Any help on this issue is greatly appreciated as it requires a lot of rework of the source code =).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.