
Show HN: A Unixy approach to WebSockets - joewalnes
http://websocketd.com/
======
jeremiep
> Write programs to handle text streams, because that is a universal
> interface.

I always hated that part of UNIX. It would be so much better if programs could
handle data-structure streams instead. Having text streams causes every single
program to implement ad-hoc parsers and serializers.

I now use the command-line only for trivial one-time actions. For everything
else I'll use a scripting language and forget about reading and writing text
streams altogether.

~~~
dkarapetyan
How exactly would that work? How would you pass one kind of data structure
from one program to another so that they both could understand it without
involving parsers or deserializer? To be concrete let's start with the
simplest possible data structure the linked list.

~~~
sparkie
S-expression

~~~
dkarapetyan
And how do you convert any data structure to an s-expression? You serialize
the data. How do you get the s-expression into a form your program can
understand? You parse it.

In other words you still haven't solved the fundamental problem of passing
data back and forth between different programs. In fact if you are going to
mandate a specific serialization/deserialization format then JSON, XML, or
even ASN.1 are better options than s-expressions.

~~~
sparkie
My point was more, let your language do the parsing and deserialization for
you. S-expressions are merely a textual representation of linked lists. The
parsing and evaluation of text is already written as part of the language.

The other point was that we're ultimately stuck with serial forms of
communication, be it wires, pipes, sockets etc. If we want to easily transfer
structured data through these serial channels, we should probably build up our
structures from a serial primitives, and S-expressions are much more handy
than plain strings (which we may not even be able to parse without ambiguity),
or XML, JSON or whatnot. One, because the parser is already implemented as
part of the language, and secondly, because you can transfer code in addition
to data, and evaluate it on the remote end to bring into scope more
"structured" data like records.

I did try to include a bit more in the previous post, but I'd accidentally hit
save, and I was unable to edit the post afterwards

------
joewalnes
OP here. Something I forgot to mention on the page is how much this simplified
life for a server admin. Using "ps" you can see each connection, the memory
overhead, CPU usage. It's easy for a sysadmin to kill bad connections without
taking down the whole server.

~~~
comboy
I like the project. But it says:

> Full duplex messaging

And the examples only show single direction. Is my understanding correct that
everything received goes as STDIN? And is it also possible to run websocketd
as a client?

~~~
jakejake
In the source code /examples folder most of the languages have a bi-
directional example called "greeter"

------
elierotenberg
Unless you need to handle a very small number of concurrent connections, using
1 process per connection seems to be a huge overhead, although I can think of
some use cases.

However, I can imagine a similar tool doing multi/demultiplexing, eg the
handler process would take input text lines prepended with a connection
identifier (eg. "$socketID $message") and output using a similar formatting.
Pretty much like websocketd but with multiplexing and unixy/pipe-friendly (eg.
you can apply grep/awk/etc before and after).

How would this fit compared to websocketd?

~~~
ayani
Indeed, there is no free lunch.

At is stands, this is only really workable for low traffic (so it doesn't eat
memory) where connections do not come and go frequently (so it doesn't eat
process management CPU).

Once you start doing multiplexing for the sake of making this more reasonable
in terms of resource usage, the simplicity benefits kind of fall away as you
move closer to a full concurrent web framework.

I guess it really depends what you're tuning for, what your use case is, and
how much hardware budget you have to throw at the problem.

~~~
estava
BTW, there is a VM for Dart that is experimenting with different concurrent
modes to provide an alternative to async programming:
[https://github.com/dart-lang/fletch](https://github.com/dart-lang/fletch)

You can read its short wiki for some clues: [https://github.com/dart-
lang/fletch/wiki/Processes-and-Isola...](https://github.com/dart-
lang/fletch/wiki/Processes-and-Isolates)

I like Fletch's idea very much. Imagine not having to worry about Async all
the time.

Not sure how everything is implemented in Fletch, but I think I heard that in
Fletch they are able to share 1 thread per many processes if need be. And they
have been trying hard to save memory while implementing those features.

If you want to run some tests to compare with, I created some small samples
using different Dart implementations and NodeJS here:
[https://github.com/jpedrosa/arpoador/tree/master/direct_test...](https://github.com/jpedrosa/arpoador/tree/master/direct_test/simplest_http_response)

Fletch also supports a kind of Coroutine: [https://github.com/dart-
lang/fletch/wiki/Coroutines-and-Thre...](https://github.com/dart-
lang/fletch/wiki/Coroutines-and-Threads)

~~~
skrebbel
> _Imagine not having to worry about Async all the time._

I'm nitpicking, because Fletch truly sounds very cool indeed, but when I use
Elixir, Erlang, or Go, I never worry about async either. From that wiki page,
I can't really see what the difference with the Erlang VM is.

(that's a good thing, the Erlang VM is awesome, and being able to write server
code on an Erlang-like VM and still share code with the browser sounds like
_the_ thing that could make me adopt Dart)

------
Lerc
I used this a while ago on my own insane project. I had a Webdav server and
websocketd providing a Web interface to a Linux box.

Even at it's young age (bug reports #5 and #7 were mine), it allowed me to
progress a lot further in my project before I needed to write a server
designed specifically for the task at hand.

In the end I wrote userserv
[https://github.com/Lerc/userserv](https://github.com/Lerc/userserv) for the
one must have feature I needed. I needed logins and responses delivered from a
process with the UID of the CookieToken.

So, thanks Joe. Notanos got further because of websocketd and while I'm not
currently using it, There's a high chance of me doing so in future projects.

~~~
joewalnes
Great to hear! Thanks :)

------
riobard
I guess the next logical step is to build a virtual filesystem such that each
connection is represented by a file. Then you can further decouple the
connection handling from the actual application. Applications can start later
to talk to a connection established before.

~~~
nine_k
That would be less 'unixy' and more 'plan-niney' :)

I like the idea of having a FS interface, e.g. using named pipes made
available by the daemon.

------
ianbicking
I don't understand how I'd use this. WebSockets are generally for server-
initiated events, but this doesn't make it very easy to initiate events on the
server. Usually it will be some pubsub situation, so I'd want a server process
to be able to emit a message to one or many connections - but instead the
connection is tied to one process for its life. I'd like to see an example
like a simple chat room.

~~~
andrewstuart2
Actually, I think you're looking for Server-Sent Events [1] for something of a
pub/sub nature. WebSockets, on the other hand, are intended for realtime
_bidirectional_ communication over a single TCP connection.

Pub/Sub is much more one-directional. A single subscribe event initiates a
stream of all published events. Server-Sent Events are more suited for this
because the server will never have to bother checking the TCP connection for
incoming data.

[1] [http://www.w3.org/TR/2011/WD-
eventsource-20110208/](http://www.w3.org/TR/2011/WD-eventsource-20110208/)

------
NathanOsullivan
A project in a similar vein is websockify (
[https://github.com/kanaka/websockify](https://github.com/kanaka/websockify) )
, which makes an existing app with a TCP endpoint available over websockets.

~~~
espadrine
What are the differences? Do I miss anything?

1\. websocketify allows binary, while websocketd is text-only

2\. websocketify does I/O through a socket that the program uses (and that it
sniffes through rebind.so), while websocketd relies on stdin / stdout

3\. The arguments are different

websocketd --port=8080 my-program

websocketify 8080 -D -- my-program

~~~
asergeyev
Also websocketd is multiplatform and does not require python. Neither good or
bad, just another difference :)

Also, not sure if websocketify can but websocketd also has --dir argument and
can supervise and route many sockets to many different programs.

~~~
cpncrunch
websockify can be run on Node.js as well as python. Both versions seem to be
highly reliable (I'm using them on multiple production servers with paying
customers for web conferencing). The python version doesn't seem to work on
Windows servers, but the Node version works fine.

------
kragen
> Each inbound WebSocket connection runs your program in a dedicated process.
> Connections are isolated by process.

That sounds bad; it _is_ like “CGI, twenty years later”, as they say. In 2000
at KnowNow, we were able to support over ten thousand concurrent Comet
connections using a hacked-up version of thttpd, on a 1GHz CPU with 1GiB of
RAM. I’ll be surprised if you can support ten thousand Comet connections using
WebSockets and websocketd even on a modern machine, say, with a quad-core 3GHz
CPU and 32GiB of RAM.

Why would you want ten thousand concurrent connections? Well, normal non-Comet
HTTP is pretty amazingly lightweight on the server side, due to REST. Taking
an extreme example, this HN discussion page takes 5 requests to load, which
takes about a second, but much of that is network latency — a total of maybe
½s of time on the server side. But it contains 7000 words to read, which takes
about 2048 seconds. So a single process or thread on the server can handle
about 4096 concurrent HN readers. So a relatively normal machine can handle
hundreds of thousands of concurrent users without breaking a sweat.

On the other hand, Linux _has_ gotten a _lot_ better since 2000 at managing
large numbers of runnable processes and doing things like fork and exit.
httpdito
([http://canonical.org/~kragen/sw/dev3/server.s](http://canonical.org/~kragen/sw/dev3/server.s))
can handle tens of thousands of hits on a single machine nowadays, even though
each hit forks a new child process (which then exits).
[http://canonical.org/~kragen/sw/dev3/httpdito-
readme](http://canonical.org/~kragen/sw/dev3/httpdito-readme) has more
performance notes.

On the gripping hand, httpdito’s virtual memory size is up to 16kiB, so Linux
may be able to handle httpdito processes better than regular processes.

~~~
asergeyev
Difference with cgi is that they are live and die, with each user request.
websocketd programs are more like "one per user session"... Makes sense when
your user sessions are lengthy.

~~~
kragen
Yes — that’s exactly the problem. Handling ten thousand concurrent users with
CGI is easy, even on 2000-era hardware. Ten thousand concurrent users might be
four requests per second. But ten thousand concurrent users using websocketd
means you have ten thousand processes. And if you’re doing some kind of
pub/sub thing, every once in a while, all of those processes will go into the
run queue at once, because there’s a message for all of them. Have you ever
administered a server with a load average over 1000?

Still, the O(N) scheduler work in current Linux might make that kind of thing
survivable.

~~~
joewalnes
Yes, I often have 10K plus processes running on a production server. It's
caused troubles at time due to misbehaving processes, but mostly it's been ok.
Linux is surprisingly good at this (wasn't always the case).

For the times when some of my processes were misbehaving, it was easy to
identify which processes were misbehaving with "ps", "top", etc and resolve
with "nice", "kill". This killed the bad connections without bringing the rest
of the app down. Sysadmins like me.

~~~
kragen
Have you been waking up all 10K of them at a time? Handling 10,000 sleeping
processes is not so surprising.

------
detaro
Looks very nice for quick prototype kind of things in any language or to make
existing tools quickly available to a small circle of users!

for larger-scale usage the overhead probably is to big.

~~~
joewalnes
OP here. The overhead is really dependent on your process overhead. I
typically use it for Python/Ruby/C apps which are relatively lightweight
compared to something JVM based.

The runtime profile of WebSockets tends to be different to typical HTTP
requests too. With typical HTTP you're often optimizing for 1000s of short
requests per second, whereas with WebSockets the requests are much longer
lived.

~~~
jeremiep
The JVM is optimized for long-running processes as it takes time for the JIT
to kick in. Other than the memory footprint your code gets quite a performance
boost; I'm always surprised how much of a difference it makes before and
after.

You wouldn't want to boot a JVM process per connection but rather implement
the listening socket yourself and dispatch connections within that same
process with all of the required services already initialized. A WebSocket
server is no different.

------
vram22
I had come across websocketd a while ago, and to try it out, wrote a simple
Python program and a web page. The two posts about it are here:

1\. Use WebSockets and Python for web-based system monitoring:

[http://jugad2.blogspot.in/2014/01/use-websockets-and-
python-...](http://jugad2.blogspot.in/2014/01/use-websockets-and-python-for-
web-based.html)

2\. websocketd and Python for system monitoring - the JavaScript WebSocket
client:

[http://jugad2.blogspot.in/2014/01/websocketd-and-python-
for-...](http://jugad2.blogspot.in/2014/01/websocketd-and-python-for-
system.html)

Note: As it says in one of the posts, you have to:

set PYTHONUNBUFFERED=true

for the Python program to work as a websocket server, though it works fine
without that if only run directly at the command line (without websocketd).
Thanks to Joe for pointing this out.

websocketd is a nice utility.

------
Udo
This is a good idea.

Myself, I noticed that almost all my websockets projects could easily share
one single code base, and finally I just made a Websockets boilerplate repo
that I can pull from for any given project. This is what node.js really excels
at, and it's an execution model fundamentally different from websocketd: being
a message broker between the client and the request-based web server.

Some people don't like the separation between web server and websockets
server, but when you think about it they don't belong quite on the same level
of abstraction. Plus, it's usually orders of magnitude easier to reason about
single requests than to reason about a complex persistent application server's
state.

------
lordlarm
For simple one-directional communication (Server to client(s)) as shown in the
example, it may for many people be simpler to use EventSource [1]

I've used this for UI's where the server continuously sends/pushes updates to
the clients. Really handy, and multiple implementations and libraries
available in most languages.

Of interest is perhaps also the spec [2]

[1]: [https://developer.mozilla.org/en-
US/docs/Web/API/EventSource](https://developer.mozilla.org/en-
US/docs/Web/API/EventSource)

[2]: [https://html.spec.whatwg.org/multipage/comms.html#the-
events...](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-
interface)

~~~
asergeyev
Not supported in any of IE versions...
[https://status.modern.ie/serversenteventseventsource?term=Ev...](https://status.modern.ie/serversenteventseventsource?term=EventSource)

And WebSockets is working since IE10
[https://status.modern.ie/websocket?term=WebSocket](https://status.modern.ie/websocket?term=WebSocket)

~~~
stephenr
Due to it's nature, EventSource can be quite easily implemented using a
polyfill for legacy browsers.

It's also MUCH easier to setup/run in a multi-layered stack (e.g.
Pound/HAProxy > Varnish > App Server).

As per usual, WebSockets is what the "cool kids" use, even when it's often
much less appropriate and much less flexible.

~~~
tokenizerrr
So what would you use to implement the polyfill for IE10? Is there something
more appropriate than websockets? If not, are we not back to square one where
you may as well use websockets from the start?

~~~
stephenr
There are multiple polyfills for EventSource - mostly all it needs is a
working XHR implementation.

The key thing is that a proxy/load balancer doesn't need special cases to
support EventSource like it needs (and often doesn't have) with websockets.

~~~
tokenizerrr
And then you're polling instead of pushing.

~~~
stephenr
In one browser.

If your server side stack (https terminator, load balancer, caching proxy,
app/web server) can't support websockets, you'll be falling back to long
polling in _every_ browser.

~~~
tokenizerrr
No, you are polling for every IE user. Which is likely a majority of your
visitors. You can control your server side stack, you can't control your
visitor's browser.

My argument is that since IE does not support EventSource a large portion of
your userbase will have to use an alternative implementation. If this
implementation polls then you get minimal gain since now the majority of your
userbase is polling again.

~~~
stephenr
It isn't 2003, IE is not likely to be a majority of visitors any more.

Caniuse.com reports 75% global relative support for EventSource and just 85%
for websockets.

To claim that "you can control your stack" as a solution to a protocol that
has well known issues with multiple components in the path between a client
and the server is somewhat naive frankly.

As for the "pull vs push" debate: event source lets you open a channel to a
resource on the server and wait for it to send you data.

XHR long polling works exactly the same way - you open an asynchronous
connection and wait for data, the only difference is you need to handle
reconnects, and the message parsing is done in user land code rather than in
c/whatever.

Long polling is definitely not about making a new xhr request every couple of
seconds to check for more data.

~~~
asergeyev
There is nothing wrong with server side events. I even played with them
already.

But for bi-directional communication we do have to use websockets and it's the
good tool for the job.

If you'd like to see maybe event source is something that could be added to
websocketd as additional protocol, start conversation in issues, nothing is
wrong with pitching an idea. We already got cgi and static html because it's
not always about websockets, sometimes you want other things handy.

------
minimax
The thing with WebSockets is that they are message oriented. WebSockets
endpoints are presented with a series of distinct messages, whereas
stdin/stdout are stream based and you have to build messaging on top, if
that's what you want. I guess the idea here is that you are just using '\n' as
the message delimiter?

~~~
asergeyev
Pretty much. Good example is Joe's vmstats [https://github.com/joewalnes/web-
vmstats](https://github.com/joewalnes/web-vmstats)

------
jkarneges
Elegant approach. Reminds me of inetd.

~~~
colanderman
I'm surprised this _isn 't_ just implemented as an inetd handler, given the
"do one thing and do it well" mantra.

~~~
fit2rule
Its the 21st Century. People don't look backwards any more. inetd is passé..

~~~
colanderman
I'm assuming you're being sarcastic. This tool wants to "do things the Unix
way"... which was invented in the 1970s.

------
romanpoet
Why is this better than netcat?

[https://en.wikipedia.org/wiki/Netcat](https://en.wikipedia.org/wiki/Netcat)

~~~
colanderman
Because it handles the WebSocket protocol for you.

------
keyle
Oh my! I have been waiting for this, trying to write my own websocket server
implementation in multiple esoteric languages! Fantastic!

------
aabajian
This looks great, without knowing much I was able to get:

./websocketd --staticdir=. --port=8123 ps

to give me a simple output of processes. What I'd really LOVE is to get this
to work:

./websocketd --staticdir=. --port=8123 htop

It'd be great if I could see the output of htop on the web...from anywhere. I
guess htop is setting up a different video mode or something that isn't
compatible?

~~~
olalonde
I think you would need to feed what you are getting to a Javascript terminal
emulator. (e.g.
[https://github.com/chjj/tty.js/](https://github.com/chjj/tty.js/))

------
runewell
This is perfect for admin tools. I'd rather parse stdout strings than code a
custom API.

------
frknbasaran
I'll try this. Looks good!

------
angersock
Oh, this is going to be so fun for doing terrible things.

~~~
asergeyev
Untold rule is not to run "sudo websocketd --devconsole bash" :)

~~~
cturner
The browser will not implement terminal controls. You could get by though.
This sort of scenario is why ed is never obsolete.

~~~
joewalnes
ed is for the weak. Real developers edit bits using the magnetic forces of
oceans.

------
hurin
Could anyone give an example of what this is used for?

~~~
runewell
It appears to me that it is useful for eliminating the "middle man"
scripts/code often written for web application and server interaction. If I
want to create a web application that displays the status of my server (CPU,
memory, I/O, etc) I can use this software to call vmstat directly instead of
writing an API (PHP, Node.js, RoR, etc) that executes the same command or uses
a library that requires even more code to get the exact same data.

~~~
hurin
I'm sure this this is probably because I don't have do this for work - but
from that description it's hard for me to tell what the advantages are. A
script to feed vmstat (or other command data) with a python socket would be
~50 lines or so.

------
dkarapetyan
This is cool. I can see this being used for prototyping and validating an idea
before going full steam ahead with a more scalable framework.

------
MatthewWilkes
It's a petty comment, I know, but:

    
    
        for count in range(0, 10):
          print count + 1
          sleep(0.5)
    

makes me feel sad.

~~~
asergeyev
Please ignore complexity of supplied examples... It's really not for that kind
of use. I have lots of scripts that do something like this (and most of them
are not in bash :)...):

    
    
        while read ARG; do
              if validate $ARG; then
                    run_something $ARG
              fi
        done
    

Most of my "validate" pieces are bash functions and most "run_something"
return lengthy CSV datablocks.

Then, there is the javascript that uses user mouse (and other signals) to
generate arguments to send to websocketd and draw pretty visuals based on data
that arrives back.

------
spiralpolitik
I assume you are treating `stdin` and `stdout` as just a stream of bytes
rather than trying to do anything clever a la Python 3 ?

~~~
joewalnes
Yep. The only magic thing is a line break (e.g. \n) is used to terminate each
message.

------
cypher543
> "Write programs that do one thing and do it well."

So why does this WebSocket daemon also serve static files and CGI
applications?

~~~
spoiler
Because you need to implement a tiny HTTP server for WebSockets anyway (if you
want't them to work from browsers). So, adding the ability to serve static
files on top of that is trivial.

Not sure why they added CGI, though.

~~~
joewalnes
That indeed was a dilemma. Although it focusses on doing one thing well, there
were also a few core things that are useful to support a websocket based app -
namely a place to serve the actual web content from (static) and the ability
to use websocketd using vanilla http instead of websockets (cgi).

------
tomerbd
beautiful website may I ask with which template / framework it was written?
(is it in github by any chance?)

~~~
joewalnes
Didn't use any templates or frameworks (apart from font-awesome for icons and
prism for syntax highlighting the code examples).

Just some good old fashioned hand written HTML + CSS.
[https://github.com/joewalnes/websocketd/tree/gh-
pages](https://github.com/joewalnes/websocketd/tree/gh-pages)

------
nichochar
Holy shit that's cool

------
kalmi10
How are message boundaries handled?

WebSockets are message-based. UNIX streams are not.

~~~
joewalnes
websocketd treats each line as a message. i.e. boundaries are marked with \n

------
nubb
Link is down =(

------
curiously
this is _fucking_ cool. I got it instantly just glancing at the code example.
I immediately understood how it worked. That is powerful stuff.

My only worry of course is, how would you scale this up? What's really going
beneath the hood.

I'm really excited and trying to think of something so I can use it as an
excuse to use this.

The only other suggestion I would make is maybe change the name to something
more catchy and brandable. Websocketd...okay like systemd...but I don't know,
something as good as this deserves a brandable name like Jupiter, or some
Greek goddess or clever hacky name.

~~~
joewalnes
Thanks. And I suck at naming stuff.

