Socketioxide - Here to define the Realtime. (0.6 release)

Introducing Socketioxide 0.6

Rust is hard, so is writing networking code in it. But who doesn't want its blazing fast speed? Socketioxide fills the gap. It is socket.io, but with speed and safety, that helps you achieve unimaginable while keeping it dead simple. Its the Simplicity teaming up with Speed.

OK GUYS. I know I am selling pancakes in the house of pancakes. But at least check it out. It has some serious enterprise grade testing done before each release, and it won't crash your prod server. And it has almost all feats specified in the protocol.

TLDR

Socketioxide is a socket.io server implementation in Rust, that integrates with the Tower ecosystem and the Tokio stack. Consider starring on Github and sharing with your co-workers!

New Features:

  1. socketioxide

  • New API for creating the socket.io layer/service. A cheaply clonable SocketIo struct is now returned with the layer/service and allows to access namespaces/rooms/sockets everywhere in the application. Moreover, it is now possible to add and remove namespaces dynamically through the SocketIo struct.
  • The socket.io v4 protocol is now available under the feature flag v4, it matches every socket.io js version from 1.0.3 to current . The v5 protocol is still the default and is more performant, it matches every socket.io js version from v3.0.0 to current.
  1. engineioxide

  • The socket parameter for the handler is now an Arc<Socket>.
  • The max_payload option is now applied when encoding a packet. Before, it was only applied when decoding a packet.
  • With websocket transport, packets are now bufferred before being flushed. Before, they were flushed one by one.

1 Like

Release Note: Release v0.6.0 · Totodore/socketioxide · GitHub

I just came here to applaud this quip.

And, uh, even when socket.io was introduced as a solution to a problem, it was clear to me that it was a problem I did not personally have. Managing sockets is not rocket science, or computer science for that matter.

My only feedback is kind of critical, but also a bit of pedantry. I cannot stand asynchronous callback-based APIs. This architecture makes flow control intolerably convoluted.

It also makes state manipulation difficult to express in Rust without interior mutability. That in turn leads to some problems like deadlocking when trying to acquire aliasing RefMut<T> with socket.extensions.get_mut(). It's easier to do by accident than one might think, reentrancy or just at varying levels of the call stack.

Dumb thing to do for demonstration purposes:

println!("HERE BE DRAGONS!");

let m1: RefMut<Nickname> = socket.extensions.get_mut().expect("m1 failed");
let m2: RefMut<Nickname> = socket.extensions.get_mut().expect("m2 failed");

println!("This deadlocks: {m1:?}, {m2:?}");

Deadlocks on the let m2 line. If multiple threads of execution (async functions) have access to the same extensions, the results could be disastrous and unenviably difficult to debug.


Is there a better way? Using an event stream might be. A stream of events that I can consume and handle on my own terms. Flow control is obvious, it's just a normal loop which might call normal subroutines. No wondering if event handlers will be called concurrently and interleaved in non-deterministic ways at await points. Thus, state management can be self-contained and doesn't have to "cross the streams". I could put all of my state on the stack if I wanted to, rather than plugging it into what is essentially thread-local storage (extensions).

Even better, I can compose event streams, filter uninteresting events, map them into different types (or even map them to their own event handlers if I wanted to be cute). It's much harder to compose asynchronous callbacks.

Sorry, I don't really know how useful this is. But it's all I got.

4 Likes

Thanks a lot for the criticisms. I will pass it to the maintainer.

Hey ! Thanks a lot for your feedback.

My API design decisions were based on two things :

  • The socket.io js api (I wanted to mimic as much as possible the api)
  • Axum-like apis. I know that i'm missing most of it, like extractors but that is why I implemented async callback by default and extensions (which come from hyper).

For the async callback based Api, I could make handlers non synchronous but it would be quite verbose because most of the time the user would spawn a tokio::task to do some async stuff (like call to db, or even just call to socket.io itself with an async multi node adapter).

Indeed, Extensions are backed by a DashMap and it might provoque a lot of issues because of deadlocks. I don't think I can find a magic way to avoid that but I'll write disclaimers in the documentation of Extensions.

It is a good idea to propose a stream implementation, I think both patterns are not incompatible in the same library. The socket could implements Into<Stream> or even directly the Stream trait.

I personally disagree (maybe I'm not good enough to see that it is not computer science). But let's take an server with multiple instances + multiple layers of multiplexing, it is much much faster (in terms of time) to use socket.io rather than making an entire system to share state + handling every socket things. And maybe newbies who come from the js world will be happy to move their favourite socket.io system in rust. However, I agree that it might not be the most performant solution compared to a specific system made for a specific need and that someone with a good experience in dev/rust/sockets might prefer using raw websockets.

1 Like

I can see that. The socket.io API is an artifact of history. nodeJS did not support Promises at the time [1], all nodeJS APIs required async callbacks because that's the only tool they had at their disposal. Things are quite different now, and if socket.io had been designed today, I'm certain it could also benefit from async streams as a first-class API.

Yes, this claim could do with some justification on my part. By "computer science" I mean the more rigorous application of the scientific method to computing, rather than the practical and pragmatic approach of computer engineering. The CS side is more about generalizing problems to find an appropriate solution (in my interpretation, anyway). For instance, "managing sockets" is a niche of "managing state", which itself could be decomposed into data structures and algorithms.

Not that this attempt to clarify helps the conversation, or anything. But that's what I was getting at: We have access to many data structures and algorithms that are quite adequate for doing useful things with sockets.

I think this makes sense as a value proposition. Simplifying challenges is always welcome!

Then again, if users are facing challenges with multiplexing multiple instances, they are already deep in distributed computing land, and they will have other challenges that a socket.io style library won't address. And this touches on the other side of my "computer science" comment. These are network engineering and systems administration challenges [2].


  1. The lack of Promise support is one of the "10 things" Ryan Dahl, creator of nodeJS, has publicly admitted was a mistake. https://youtu.be/M3BM9TB-8yA?si=mtSURKWM8ImksOoF&t=305 ↩︎

  2. I won't even go into eventual consistency within distributed computing. This may also be a concern depending on architectural decisions. ↩︎

1 Like

Folks who come to Rust from JS or Python, to them, more than anything, socketioxide will be a convenient way to work with sockets. Its event callback based style makes it very easy to design an architecture quite easily, which cannot be said for plain websockets libraries, you have to take care of ton of things - heartbeat, reconnection, state recovery, adapter and I can go on; and these things are not necessarily easy to get it right in a complex language like Rust.

"Data structures can take care of it" is an oversimplification of the problem. Most people who want to work with websockets don't know, neither care about data structures/managing sockets or implementing the said features by themselves. Which goes to show why a lot of high level server frameworks like actix, rocket and axum popping up, even everything can be done manually using a low level http server. Most folks want a managed solution, which socket.io is, a convenient way to build your product fast.

You would argue that for a seasoned dev setting everything up for themselves is pretty easy, but it isn't only the seasoned devs that matters for the community. There will be new folks who will come from easier languages and with not so much ground on programming.

1 Like

I apologize. My intention was not to argue the merits of the project. Just to explain why I personally have nothing to contribute other than the one criticism of API design. I see now it was a doomed endeavor.

1 Like

I reacted to your choice of post title "Socketioxide - Here to define the Realtime. (0.6 release)". What do you mean by realtime here exactly? It is not mentioned anywhere in the post itself.

As someone who works in safety critical hard realtime (industrial machine control software), the word "realtime" has a very specific meaning. And anything to with normal sockets over the normal Internet is never going to be able to be realtime.

Realtime is about ensuring you are able to react to something within a guaranteed deadline. Often within single digit milliseconds. That is simply not possible for anything on a general purpose network.

Or are you just using it as a buzzword? If so, please don't. Buzzwords adds nothing of meaning and just confuses the discussion.

3 Likes

Socket.IO is considered realtime because it enables instant communication between a client and a server. It achieves this through the use of WebSockets, which allows for bidirectional and full-duplex channels.
This persistent connection is crucial for realtime applications like chat rooms, online gaming, live streaming etc. With Socket.IO, when a client sends a message, it is instantly received by the server, and vice versa.
To summarize: In the context of web apps, it can be considered near-realtime or realtime.

For what it’s worth, I understand they are using the word "real-time" like in "real-time web": Real-time web - Wikipedia
You are not talking about the same concept in my opinion.

The difference with real-time computing is defined as:

The real-time web is different from real-time computing in that there is no knowing when, or if, a response will be received. The information types transmitted this way are often short messages, status updates, news alerts, or links to longer documents. […]

2 Likes

Back in the days before web sockets web pages were a matter of request-response. A page page arrives because the user requested it, updates happened because the use clicked a button or whatever. If the server had an update for some data the users did not know about it until they did something.

One could get those updates to the users page quicker by using Javascript to periodically poll the server for updates.

Then came web sockets. Now the clients can have permanently open connections to the servers and the servers can send updates to the clients any time they please. Altogether more efficient and closer to live updates.

That is when the web world started to annoy me by misusing "real time".

2 Likes

Had me in the first half, not gonna lie.