Dylan North

Dylan North

1565066763

WebSocket Simplified

Originally published by Mohd Shad Mirza at iamshadmirza.hashnode.dev

What is a WebSocket?

WebSocket allows a user to send and receive messages to a server. So basically, this is a way of communication between Client and Server. Let's understand this communication first, we will return to WebSocket in a while.

Client and Server

Web browsers (Client) and servers communicate via TCP/IP. Hypertext Transfer Protocol (HTTP) is the standard application protocol on top of TCP/IP supporting requests (from the web browser) and their responses (from server).

How does this work?

Let's go through these simple steps:-

  1. Client (browser) sends a request to the Server.
  2. A connection is established.
  3. The server sends back the response.
  4. A client receives the response.
  5. The connection is closed.

This is basically how the communication between Client and Server works. Now get a closer look at step no. 5.

Connection is closed.

The HTTP request has served its purpose and it is no longer needed, hence the connection is closed.

What if Server wants to send a message to Client?

The connection must be established successfully to start communicating. The solution here is that the Client will have to send another request to establish a connection and receive the message.

How will the client know that the server wants to send a message?

Consider this example:-

Client is starving and has ordered some food online. He is making one request per second to check if the order is ready.

0 sec: Is the food ready? (Client)
0 sec: No, wait. (Server)
1 sec: Is the food ready? (Client)
1 sec: No, wait. (Server)
2 sec: Is the food ready? (Client)
2 sec: No, wait. (Server)
3 sec: Is the food ready? (Client)
3 sec: Yes sir, here is your order. (Server)

This is what you call HTTP Polling.

Client makes repeated requests to the server and checks if there is any message to receive.

As you can see, this not very efficient. We are using unnecessary resources and the number of failed requests is also troublesome.

Is there any way to overcome this issue?

Yup, there is a variation of polling technique that is used to overcome this deficiency and it is called as Long-Polling.

Long Polling basically involves making an HTTP request to a server and then holding the connection open to allow the server to respond at a later time (as determined by the server).

Consider the Long-Polling version of the above example:-

0 sec: Is the food ready? (Client)
3 sec: Yes sir, here is your order. (Server)

Yay, problem solved.

Not exactly. Although Long Polling works, it is very expensive in terms of CPU, memory, and bandwidth (as we are blocking resources in holding the connection open).

What to do now? It looks like things are getting out of hand. Let's reach back to our savior: WebSocket.

Why WebSocket?

As you can see, Polling and Long-Polling are both quite expensive options in order to emulate real-time communication between Client and Server.

This performance bottleneck is the reason why you would want to use WebSocket instead.

WebSockets don’t need you to send a request in order to respond. They allow bidirectional data flow so you just have to listen for any data.

You can just listen to the server and it will send you a message when it’s available.

Let's look at the performance side of the WebSocket.

Resource Consumption

The chart below shows the bandwidth consumption differences between WebSockets vs Long Polling in a relatively common use case:

The difference is huge (for a relatively higher number of requests).

Speed

Here are the results for 1, 10 and 50 requests served per connection in one second:

As you can see, making a single request per connection is about 50% slower using Socket.io since the connection has to be established first. This overhead is smaller but still noticeable for ten requests. At 50 requests from the same connection, Socket.io is already 50% faster. To get a better idea of the peak throughput we will look at the benchmark with a more extensive number (500, 1000 and 2000) of requests per connection:

Here you can see that the HTTP benchmark peaks at about~950 requests per second while Socket.io serves about ~3900 requests per second. Effective, right?

Note: Socket.io is a JavaScript library for real-time web applications. It implements WebSocket internally. Consider this as a wrapper for WebSocket which provides many more features (Next Blog post of this series explains Socket.io in detail).

How does WebSocket work?

These are the steps involved in establishing a WebSocket connection.

  1. The client (browser) sends an HTTP request to the Server.
  2. A connection is established via the HTTP protocol.
  3. If the server supports the WebSocket protocol, it agrees to upgrade the connection. This is called handshake.
  4. Now that the handshake is complete the initial HTTP connection is replaced by a WebSocket connection that uses the same underlying TCP/IP protocol.
  5. At this point, data can flow back and forth freely between Client and Server.

Let's code

We are going to create two files: one server and one client.

First create a simple <html> document named as client.html containing a <script> tag. Let's see how it looks:-

Client.html

<html>
 
<script>
    // Our code goes here
</script>
 
<body>
    <h1>This is a client page</h1>
</body>
 
</html>

Now create another file server.js. Now import HTTP module and create a server. Make it listen to port 8000.

This will work as a simple http server listening to port 8000. Let's look at that too:-

Server.js

//importing http module
const http = require('http');
 
//creating a http server
const server = http.createServer((req, res) => {
    res.end("I am connected");
});
 
//making it listen to port 8000
server.listen(8000);
run command node server.js to start the listening to port 8000.
Note:- You can choose any port as you like, I just chose 8000 for no specific reason.

Our basic setup of the client and server is done now. That was simple, right? Let's get to the good stuff now.

Client setup

To construct a WebSocket, use the WebSocket() constructor which returns the websocket object. This object provides the API for creating and managing a WebSocket connection to the Server.

In simple words, this websocket object will help us establish a connection with the server and create a bi-directional data flow i.e. send and receive data from both ends.

Let us see how:-

<html>
 
<script>
    //calling the constructor which gives us the websocket object: ws
    let ws = new WebSocket('url'); 
</script>
 
<body>
    <h1>This is a client page</h1>
</body>
 
</html>

The WebSocket constructor expects a URL to listen to. Which in our case, is 'ws://localhost:8000' because that's where our server is running.

Now, this is might be a little different from what you are used to. We are not using the HTTP protocol, we are using WebSocket protocol. This will tell the client that 'Hey, we are using websocket protocol' hence 'ws://' instead of 'http://'. Simple enough? Now let's actually create a WebSocket server in server.js.

Server setup

We are gonna need a third-party module ws in our node server to use the setup the WebSocket server.

First, we will import the ws module. Then we will create a websocket server and hand it the HTTP server listening to port 8000.

HTTP server is listening to port 8000 and WebSocket server is listening to this HTTP server. Basically, it is listening to the listener.

Now our websocket is watching for traffic on port 8000. It means that it will try to establish the connection as soon as the client is available. Our server.js file will look like this: -

const http = require('http');
//importing ws module
const websocket = require('ws');
 
const server = http.createServer((req, res) => {
    res.end("I am connected");
});
//creating websocket server
const wss = new websocket.Server({ server });
 
server.listen(8000);

As we have discussed before:

WebSocket() constructor returns a websocket object provides the API for creating and managing a WebSocket connection to the Server.

Here, the wss object will help us listen to the Event emitted when a certain thing happens. Like the connection is established or handshake is complete or the connection is closed, etc.

Let's see how to listen to the messages:-

const http = require('http');
const websocket = require('ws');
 
const server = http.createServer((req, res) => {
    res.end("I am connected");
});
const wss = new websocket.Server({ server });
//calling a method 'on' which is available on websocket object
wss.on('headers', (headers, req) => {
    //logging the header
    console.log(headers);
});
 
server.listen(8000);

The method 'on' expects two arguments: Event name and callback. Event name to recognize which Event to listen/emit and callback specifies what to do with it. Here, we are just logging the headers Event. Let's see what we got:-

This is our HTTP header and I want you to be inquisitive about it because this is exactly what's going on behind the scenes. Let's break it down to understand better.

  • First things you will notice is that we got the status code 101. You may have seen 200201404 status code but this looks different. 101 is actually the Switching Protocols status code. It says "Hey, I wanna upgrade".
  • Second line shows the Upgrade information. It specifies that it wants to upgrade to websocket protocol.
  • This is actually what happens during the handshake. The browser uses the HTTP connection to establish the connection using HTTP/1.1 protocol and then it Upgrade it to websocket protocol.

Now this will make more sense.

Headers Event is emitted before the response headers are written to the socket as part of the handshake. This allows you to inspect/modify the headers before they are sent.
This means you can modify the Header to accept, reject or anything else as you like. By default, it accepts the request.

Similarly, we can add one more event connection which is emitted when the handshake is complete. We will send a message to the Client upon successfully establishing a connection. Let's see how:-

const http = require('http');
const websocket = require('ws');
 
const server = http.createServer((req, res) => {
    res.end("I am connected");
});
const wss = new websocket.Server({ server });
 
wss.on('headers', (headers, req) => {
    //console.log(headers); Not logging the header anymore
});
 
//Event: 'connection'
wss.on('connection', (ws, req) => {
    ws.send('This is a message from server, connection is established');
    //receive the message from client on Event: 'message'
    ws.on('message', (msg) => {
        console.log(msg);
    });
});
 
server.listen(8000);

We are also listening for the event message coming from Client. Let's create that:-

<html>
 
<script>
    let ws = new WebSocket('url'); 
    //logging the websocket property properties
    console.log(ws);
    //sending a message when connection opens
    ws.onopen = (event) => ws.send("This is a message from client");
    //receiving the message from server
    ws.onmessage = (message) => console.log(message);
</script>
 
<body>
    <h1>This is a client page</h1>
</body>
 
</html>

This is how it looks in the browser:-

The first log is WebSocket listing all the properties on websocket object and the second log is MessageEvent which has data property. If you look closely, you will see that we got our message from the server.

The server log will look something like this:-

We got the client's message correctly. This marks that our connection was established successfully. Cheers!

Conclusion

To sum up, let's go through what we learned:-

  • We have covered how HTTP server works, what is Polling, Long Polling.
  • What are WebSockets and why we need them.
  • We covered how they work behind the scene and got a better understanding of the header.
  • We created our own Client and Server and successfully established the connection between them.

This is the basics of WebSockets and how they work. The next post in the series will cover socket.io and the working in more detail. We will also see why exactly we need socket.io when things are working just fine with only native WebSocket(). Why use a heavy bloated library when we can successfully send and receive messages just fine?

Originally published by Mohd Shad Mirza at iamshadmirza.hashnode.dev

=============================

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Learn More

☞ NestJS Zero to Hero - Modern TypeScript Back-end Development

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Complete Next.js with React & Node - Beautiful Portfolio App

☞ Angular & NodeJS - The MEAN Stack Guide

☞ NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

☞ Docker for Node.js Projects From a Docker Captain

☞ Intro To MySQL With Node.js - Learn To Use MySQL with Node!

☞ Node.js Absolute Beginners Guide - Learn Node From Scratch

#web-development #serverless #javascript #node-js

What is GEEK

Buddha Community

WebSocket Simplified

SpringBoot + WebSocket Across Multiple Applications

Spring.io WebSocket guide: https://spring.io/guides/gs/messaging-stomp-websocket/

This post is highly referenced from the WebSocket guide above by spring.io. The difference is, in this guide, I will show you how to do it across multiple applications. When you use microservices architecture, usually you will have an app as a “front” containing a user interface, and an app on the “back” to process business logic and database-related operations. For example, the use case is: you want your “back” app to send a notification to “front” when the order is completed so that the user can get a pop-up notification alert.

What I’m going to show is how to send WebSocket messages from your “back” app to the “front” app, where both apps based on SpringBoot. We will have 2 apps:

  • learn-websocket-senderas the “back”, we will call it “sender”, running on port 8090
  • learn-websocket-receiver as the “front”, we will call it “receiver”, running on port 8091

#spring-boot #stomp #websocket #java #messaging #springboot + websocket across multiple applications

Veronica  Roob

Veronica Roob

1651432260

Websocket: The Hoa\Websocket Library

Hoa is a modular, extensible and structured set of PHP libraries.
Moreover, Hoa aims at being a bridge between industrial and research worlds.

Hoa\Websocket

This library allows to manipulate the WebSocket protocol and proposes a server and a client. It supports two specifications RFC6455 and Hybi (at the same time).

Learn more.

Installation

With Composer, to include this library into your dependencies, you need to require hoa/websocket:

$ composer require hoa/websocket '~3.0'

For more installation procedures, please read the Source page.

Testing

Before running the test suites, the development dependencies must be installed:

$ composer install

Then, to run all the test suites:

$ vendor/bin/hoa test:run

For more information, please read the contributor guide.

Quick usage

As a quick overview, we propose to start a websocket server and echo messages. The class Hoa\Websocket\Server proposes six listeners: open, message, binary-message, ping, close and error. Thus:

$websocket = new Hoa\Websocket\Server(
    new Hoa\Socket\Server('ws://127.0.0.1:8889')
);
$websocket->on('open', function (Hoa\Event\Bucket $bucket) {
    echo 'new connection', "\n";

    return;
});
$websocket->on('message', function (Hoa\Event\Bucket $bucket) {
    $data = $bucket->getData();
    echo '> message ', $data['message'], "\n";
    $bucket->getSource()->send($data['message']);
    echo '< echo', "\n";

    return;
});
$websocket->on('close', function (Hoa\Event\Bucket $bucket) {
    echo 'connection closed', "\n";

    return;
});
$websocket->run();

Finally, we have to write a client in HTML and Javascript:

<input type="text" id="input" placeholder="Message…" />
<hr />
<pre id="output"></pre>

<script>
  var host   = 'ws://127.0.0.1:8889';
  var socket = null;
  var input  = document.getElementById('input');
  var output = document.getElementById('output');
  var print  = function (message) {
      var samp       = document.createElement('samp');
      samp.innerHTML = message + '\n';
      output.appendChild(samp);

      return;
  };

  input.addEventListener('keyup', function (evt) {
      if (13 === evt.keyCode) {
          var msg = input.value;

          if (!msg) {
              return;
          }

          try {
              socket.send(msg);
              input.value = '';
              input.focus();
          } catch (e) {
              console.log(e);
          }

          return;
      }
  });

  try {
      socket = new WebSocket(host);
      socket.onopen = function () {
          print('connection is opened');
          input.focus();

          return;
      };
      socket.onmessage = function (msg) {
          print(msg.data);

          return;
      };
      socket.onclose = function () {
          print('connection is closed');

          return;
      };
  } catch (e) {
      console.log(e);
  }
</script>

Here we are. All sent messages are echoed.

Awecode

The following awecodes show this library in action:

  • Hoa\Websocket: why and how to use Hoa\Websocket\Server and Hoa\Websocket\Client? A simple example will illustrate the WebSocket protocol.

Documentation

The hack book of Hoa\Websocket contains detailed information about how to use this library and how it works.

To generate the documentation locally, execute the following commands:

$ composer require --dev hoa/devtools
$ vendor/bin/hoa devtools:documentation --open

More documentation can be found on the project's website: hoa-project.net.

Getting help

There are mainly two ways to get help:

Contribution

Do you want to contribute? Thanks! A detailed contributor guide explains everything you need to know.

License

Hoa is under the New BSD License (BSD-3-Clause). Please, see LICENSE for details.

Related projects

The following projects are using this library:

  • Marvirc, A dead simple, extremely modular and blazing fast IRC bot,
  • WellCommerce, Modern e-commerce engine built on top of Symfony 3 full-stack framework.

Author: hoaproject
Source Code: https://github.com/hoaproject/Websocket

#php #websocket 

Daisy Rees

Daisy Rees

1647228893

Fastify Websocket: Basic Websocket Support for Fastify

Fastify Websocket

WebSocket support for Fastify. Built upon ws@8.

Install

npm install fastify-websocket --save
# or 
yarn add fastify-websocket

If you're a TypeScript user, this package has its own TypeScript types built in, but you will also need to install the types for the ws package:

npm install @types/ws --save-dev
# or
yarn add -D @types/ws

Usage

After registering this plugin, you can choose on which routes the WS server will respond. This can be achieved by adding websocket: true property to routeOptions on a fastify's .get route. In this case two arguments will be passed to the handler, the socket connection, and the fastify request object:

'use strict'

const fastify = require('fastify')()
fastify.register(require('fastify-websocket'))

fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
  connection.socket.on('message', message => {
    // message.toString() === 'hi from client'
    connection.socket.send('hi from server')
  })
})

fastify.listen(3000, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

In this case, it will respond with a 404 error on every unregistered route, closing the incoming upgrade connection requests.

However, you can still define a wildcard route, that will be used as default handler:

'use strict'

const fastify = require('fastify')()

fastify.register(require('fastify-websocket'), {
  options: { maxPayload: 1048576 }
})


fastify.get('/*', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
  connection.socket.on('message', message => {
    // message.toString() === 'hi from client'
    connection.socket.send('hi from wildcard route')
  })
})

fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
  connection.socket.on('message', message => {
    // message.toString() === 'hi from client'
    connection.socket.send('hi from server')
  })
})

fastify.listen(3000, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

Attaching event handlers

It is important that websocket route handlers attach event handlers synchronously during handler execution to avoid accidentally dropping messages. If you want to do any async work in your websocket handler, say to authenticate a user or load data from a datastore, ensure you attach any on('message') handlers before you trigger this async work. Otherwise, messages might arrive whilst this async work is underway, and if there is no handler listening for this data it will be silently dropped.

Here is an example of how to attach message handlers synchronously while still accessing asynchronous resources. We store a promise for the async thing in a local variable, attach the message handler synchronously, and then make the message handler itself asynchronous to grab the async data and do some processing:

fastify.get('/*', { websocket: true }, (connection, request) => {
  const sessionPromise = request.getSession() // example async session getter, called synchronously to return a promise

  connection.socket.on('message', async (message) => {
    const session = await sessionPromise()
    // do something with the message and session
  })
})

Using hooks

Routes registered with fastify-websocket respect the Fastify plugin encapsulation contexts, and so will run any hooks that have been registered. This means the same route hooks you might use for authentication or error handling of plain old HTTP handlers will apply to websocket handlers as well.

fastify.addHook('preValidation', async (request, reply) => {
  // check if the request is authenticated
  if (!request.isAuthenticated()) {
    await reply.code(401).send("not authenticated");
  }
})
fastify.get('/', { websocket: true }, (connection, req) => {
  // the connection will only be opened for authenticated incoming requests
  connection.socket.on('message', message => {
    // ...
  })
})

NB This plugin uses the same router as the fastify instance, this has a few implications to take into account:

  • Websocket route handlers follow the usual fastify request lifecycle, which means hooks, error handlers, and decorators all work the same way as other route handlers.
  • You can access the fastify server via this in your handlers
  • When using fastify-websocket, it needs to be registered before all routes in order to be able to intercept websocket connections to existing routes and close the connection on non-websocket routes.
'use strict'

const fastify = require('fastify')()

fastify.register(require('fastify-websocket'))

fastify.get('/', { websocket: true }, function wsHandler (connection, req) {
  // bound to fastify server
  this.myDecoration.someFunc()

  connection.socket.on('message', message => {
    // message.toString() === 'hi from client'
    connection.socket.send('hi from server')
  })
})

fastify.listen(3000, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

If you need to handle both HTTP requests and incoming socket connections on the same route, you can still do it using the full declaration syntax, adding a wsHandler property.

'use strict'

const fastify = require('fastify')()

function handle (conn, req) {
  conn.pipe(conn) // creates an echo server
}

fastify.register(require('fastify-websocket'), {
  handle,
  options: { maxPayload: 1048576 }
})

fastify.route({
  method: 'GET',
  url: '/hello',
  handler: (req, reply) => {
    // this will handle http requests
    reply.send({ hello: 'world' })
  },
  wsHandler: (conn, req) => {
    // this will handle websockets connections
    conn.setEncoding('utf8')
    conn.write('hello client')

    conn.once('data', chunk => {
      conn.end()
    })
  }
})

fastify.listen(3000, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

Custom error handler:

You can optionally provide a custom errorHandler that will be used to handle any cleaning up:

'use strict'

const fastify = require('fastify')()

fastify.register(require('fastify-websocket'), {
  errorHandler: function (error, conn /* SocketStream */, req /* FastifyRequest */, reply /* FastifyReply */) {
    // Do stuff
    // destroy/close connection
    conn.destroy(error)
  },
  options: {
    maxPayload: 1048576, // we set the maximum allowed messages size to 1 MiB (1024 bytes * 1024 bytes)
    verifyClient: function (info, next) {
      if (info.req.headers['x-fastify-header'] !== 'fastify is awesome !') {
        return next(false) // the connection is not allowed
      }
      next(true) // the connection is allowed
    }
  }
})

fastify.get('/', { websocket: true }, (connection /* SocketStream */, req /* FastifyRequest */) => {
  connection.socket.on('message', message => {
    // message.toString() === 'hi from client'
    connection.socket.send('hi from server')
  })
})

fastify.listen(3000, err => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

Options

fastify-websocket accept these options for ws :

  • objectMode - Send each chunk on its own, and do not try to pack them in a single websocket frame.
  • host - The hostname where to bind the server.
  • port - The port where to bind the server.
  • backlog - The maximum length of the queue of pending connections.
  • server - A pre-created Node.js HTTP/S server.
  • verifyClient - A function which can be used to validate incoming connections.
  • handleProtocols - A function which can be used to handle the WebSocket subprotocols.
  • clientTracking - Specifies whether or not to track clients.
  • perMessageDeflate - Enable/disable permessage-deflate.
  • maxPayload - The maximum allowed message size in bytes.

For more information, you can check ws options documentation.

NB By default if you do not provide a server option fastify-websocket will bind your websocket server instance to the scoped fastify instance.

NB The path option from ws should not be provided since the routing is handled by fastify itself

NB The noServer option from ws should not be provided since the point of fastify-websocket is to listen on the fastify server. If you want a custom server, you can use the server option, and if you want more control, you can use the ws library directly

Acknowledgements

This project is kindly sponsored by nearForm.

Download Details: 
Author: fastify
Source Code: https://github.com/fastify/fastify-websocket 
License: MIT
#fastify #websocket

Royce  Reinger

Royce Reinger

1658323680

EM-websocket: EventMachine Based WebSocket Server

EM-WebSocket 

EventMachine based, async, Ruby WebSocket server. Take a look at examples directory, or check out the blog post: Ruby & Websockets: TCP for the Web.

Simple server example

require 'em-websocket'

EM.run {
  EM::WebSocket.run(:host => "0.0.0.0", :port => 8080) do |ws|
    ws.onopen { |handshake|
      puts "WebSocket connection open"

      # Access properties on the EM::WebSocket::Handshake object, e.g.
      # path, query_string, origin, headers

      # Publish message to the client
      ws.send "Hello Client, you connected to #{handshake.path}"
    }

    ws.onclose { puts "Connection closed" }

    ws.onmessage { |msg|
      puts "Recieved message: #{msg}"
      ws.send "Pong: #{msg}"
    }
  end
}

Protocols supported, and protocol specific functionality

Supports all WebSocket protocols in use in the wild (and a few that are not): drafts 75, 76, 1-17, rfc.

While some of the changes between protocols are unimportant from the point of view of application developers, a few drafts did introduce new functionality. It's possible to easily test for this functionality by using

Ping & pong supported

Call ws.pingable? to check whether ping & pong is supported by the protocol in use.

It's possible to send a ping frame (ws.ping(body = '')), which the client must respond to with a pong, or the server can send an unsolicited pong frame (ws.pong(body = '')) which the client should not respond to. These methods can be used regardless of protocol version; they return true if the protocol supports ping&pong or false otherwise.

When receiving a ping, the server will automatically respond with a pong as the spec requires (so you should not write an onping handler that replies with a pong), however it is possible to bind to ping & pong events if desired by using the onping and onpong methods.

Healthchecks

It's possible to send a regular HTTP GET request to the /healthcheck endpoint and receive a 200 response from the server.

Close codes and reasons

A WebSocket connection can be closed cleanly, regardless of protocol, by calling ws.close(code = nil, body = nil).

Early protocols just close the TCP connection, draft 3 introduced a close handshake, and draft 6 added close codes and reasons to the close handshake. Call ws.supports_close_codes? to check whether close codes are supported (i.e. the protocol version is 6 or above).

The onclose callback is passed a hash which may contain following keys (depending on the protocol version):

  • was_clean: boolean indicating whether the connection was closed via the close handshake.
  • code: the close code. There are two special close codes which the server may set (as defined in the WebSocket spec):
    • 1005: no code was supplied
    • 1006: abnormal closure (the same as was_clean: false)
  • reason: the close reason

Acceptable close codes are defined in the WebSocket rfc (http://tools.ietf.org/html/rfc6455#section-7.4). The following codes can be supplies when calling ws.close(code):

  • 1000: a generic normal close
  • range 3xxx: reserved for libraries, frameworks, and applications (and can be registered with IANA)
  • range 4xxx: for private use

If unsure use a code in the 4xxx range. em-websocket may also close a connection with one of the following close codes:

  • 1002: WebSocket protocol error.
  • 1009: Message too big to process. By default em-websocket will accept frames up to 10MB in size. If a frame is larger than this the connection will be closed without reading the frame data. The limit can be overriden globally (EM::WebSocket.max_frame_size = bytes) or on a specific connection (ws.max_frame_size = bytes).

Secure server

It is possible to accept secure wss:// connections by passing :secure => true when opening the connection. Pass a :tls_options hash containing keys as described in http://www.rubydoc.info/github/eventmachine/eventmachine/EventMachine/Connection:start_tls

Warning: Safari 5 does not currently support prompting on untrusted SSL certificates therefore using a self signed certificate may leave you scratching your head.

EM::WebSocket.start({
  :host => "0.0.0.0",
  :port => 443,
  :secure => true,
  :tls_options => {
    :private_key_file => "/private/key",
    :cert_chain_file => "/ssl/certificate"
  }
}) do |ws|
  # ...
end

It's possible to check whether an incoming connection is secure by reading handshake.secure? in the onopen callback.

Running behind an SSL Proxy/Terminator, like Stunnel

The :secure_proxy => true option makes it possible to use em-websocket behind a secure SSL proxy/terminator like Stunnel which does the actual encryption & decryption.

Note that this option is only required to support drafts 75 & 76 correctly (e.g. Safari 5.1.x & earlier, and Safari on iOS 5.x & earlier).

EM::WebSocket.start({
  :host => "0.0.0.0",
  :port => 8080,
  :secure_proxy => true
}) do |ws|
  # ...
end

Handling errors

There are two kinds of errors that need to be handled -- WebSocket protocol errors and errors in application code.

WebSocket protocol errors (for example invalid data in the handshake or invalid message frames) raise errors which descend from EM::WebSocket::WebSocketError. Such errors are rescued internally and the WebSocket connection will be closed immediately or an error code sent to the browser in accordance to the WebSocket specification. It is possible to be notified in application code of such errors by including an onerror callback.

ws.onerror { |error|
  if error.kind_of?(EM::WebSocket::WebSocketError)
    # ...
  end
}

Application errors are treated differently. If no onerror callback has been defined these errors will propagate to the EventMachine reactor, typically causing your program to terminate. If you wish to handle exceptions, simply supply an onerror callback and check for exceptions which are not descendant from EM::WebSocket::WebSocketError.

It is also possible to log all errors when developing by including the :debug => true option when initialising the WebSocket server.

Emulating WebSockets in older browsers

It is possible to emulate WebSockets in older browsers using flash emulation. For example take a look at the web-socket-js project.

Using flash emulation does require some minimal support from em-websocket which is enabled by default. If flash connects to the WebSocket port and requests a policy file (which it will do if it fails to receive a policy file on port 843 after a timeout), em-websocket will return one. Also see https://github.com/igrigorik/em-websocket/issues/61 for an example policy file server which you can run on port 843.

Examples & Projects using em-websocket

  • Pusher - Realtime Messaging Service
  • Livereload - LiveReload applies CSS/JS changes to Safari or Chrome w/o reloading
  • Twitter AMQP WebSocket Example
  • examples/multicast.rb - broadcast all ruby tweets to all subscribers
  • examples/echo.rb - server <> client exchange via a websocket

Author: igrigorik
Source Code: https://github.com/igrigorik/em-websocket 
License: MIT license

#ruby #websocket #server 

Simple-websocket: Simple, EventEmitter API for WebSockets

simple-websocket    

Simple, EventEmitter API for WebSockets

features

  • super simple API for working with WebSockets in the browser
  • supports text and binary data
  • node.js duplex stream interface
  • client & server implementations

This package is used by WebTorrent.

install

npm install simple-websocket

This package works in the browser with browserify. If you do not use a bundler, you can use the simplewebsocket.min.js standalone script directly in a <script> tag. This exports a SimpleWebsocket constructor on window. Wherever you see Socket in the examples below, substitute that with SimpleWebsocket.

usage

var Socket = require('simple-websocket')

var socket = new Socket('wss://echo.websocket.org')
socket.on('connect', function () {
  // socket is connected!
  socket.send('sup!')
})

socket.on('data', function (data) {
  console.log('got message: ' + data)
})

api

socket = new Socket(url)

Create a new WebSocket connection to the server at url. This usage is a shorthand for socket = new Socket({ url: url })

socket = new Socket(opts)

If opts.url is specified as a string, then a WebSocket connection will be created to the server at opts.url.

If opts.socket is specified as an instance of a raw WebSocket object, then the given WebSocket object will be used and one will not be automatically be created internally. (This is for advanced users.)

Other properties on opts will be passed through to the underlying superclass, stream.Duplex.

socket.send(data)

Send text/binary data to the WebSocket server. data can be any of several types: String, Buffer (see buffer), TypedArrayView (Uint8Array, etc.), ArrayBuffer, or Blob (in browsers that support it).

Note: If this method is called before the socket.on('connect') event has fired, then data will be buffered.

socket.destroy([err])

Destroy and cleanup this websocket connection.

If the optional err parameter is passed, then it will be emitted as an 'error' event on the stream.

Socket.WEBSOCKET_SUPPORT

Detect WebSocket support in the javascript environment.

var Socket = require('simple-websocket')

if (Socket.WEBSOCKET_SUPPORT) {
  // websocket support!
} else {
  // fallback
}

events

socket.on('connect', function () {})

Fired when the websocket connection is ready to use.

socket.on('data', function (data) {})

Received a message from the websocket server.

data will be either a String or a Buffer/Uint8Array (see buffer). JSON strings will be parsed and the resulting Object emitted.

socket.on('close', function () {})

Called when the websocket connection has closed.

socket.on('error', function (err) {})

err is an Error object.

Fired when a fatal error occurs.

server

The server implementation is basically ws but the 'connection' event provides sockets that are instances of simple-websocket, i.e. they are duplex streams.

var Server = require('simple-websocket/server')

var server = new Server({ port: port }) // see `ws` docs for other options

server.on('connection', function (socket) {
  socket.write('pong')
  socket.on('data', function (data) {})
  socket.on('close', function () {})
  socket.on('error', function (err) {})
})

server.close()

real-world applications that use simple-websocket

  • Virus Cafe - Make a friend in 2 minutes
  • WebTorrent - The streaming torrent app
  • StudyNotes - Helping students learn faster and better
  • bittorrent-tracker - Simple, robust, BitTorrent tracker (client & server) implementation
  • instant.io - Secure, anonymous, streaming file transfer
  • lxjs-chat - Omegle chat clone
  • Metastream - Watch streaming media with friends.
  • [ your application here - send a PR ]

Author: Feross
Source Code: https://github.com/feross/simple-websocket 
License: MIT License

#websocket #node #network #javascript