Dissecting WebSocket's Overhead

, Tobias Oberstein

WebSocket itself has very low overhead, but one must be careful not to loose efficiency on lower layers.

Introduction

The WebSocket protocol itself has very low overhead. The following table lists the overhead in octets per (unfragmented) WebSocket message:

Payload Client-to-server Server-to-client
<126 6 2
<64k 8 4
<2**63 12 8

Further, WebSocket allows to transmit binary payloads without any recoding or escaping.

However, this is not the end of the story;)

The overhead when sending application data over WebSocket occurs on multiple levels:

  1. WebSocket
  2. TLS (if running secure WebSocket)
  3. TCP
  4. IP
  5. Ethernet / other

I did a couple of measurements using a JavaScript WebSocket client running in a browser that sends 8 WebSocket text messages with a string payload of length 13 characters in a loop over a GbE switched Ethernet LAN.

The client JavaScript essentially does this:

        var sock = new WebSocket("ws://192.168.1.100");

        sock.onopen = function() {
           for (var i = 0; i < 8; ++i) {
              sock.send("Hello, world!");
           }
        }
   

The server (based on Autobahn|Python and running on Twisted / FreeBSD) is configured to just echo back any WebSocket messages it receives.

Results

The results (recorded with Wireshark, see logs at the end) are somewhat surprising:

As can be seen from above numbers:

The overhead induced by TCP/IP and TLS in particular can dwarf the overhead of WebSocket itself.

When running TLS

  • all tested browsers and Autobahn|Python will produce (at least) a new TLS record on each and every WebSocket message sent
  • all tested browsers send each TLS record in a new TCP segment
  • Autobahn|Python sends multiple TLS records batched inside a single TCP segment

The browsers will send the 8 WebSocket message to the wire like this:

TCP/IP segm. | TLS record hdr | WebSocket frame hdr | WebSocket msg1 payload | TLS cipher padd. etc
TCP/IP segm. | TLS record hdr | WebSocket frame hdr | WebSocket msg2 payload | TLS cipher padd. etc
TCP/IP segm. | TLS record hdr | WebSocket frame hdr | WebSocket msg3 payload | TLS cipher padd. etc
...

Autobahn|Python sends this:

TCP/IP segment |
    TLS record hdr | WebSocket frame hdr | WebSocket msg1 payload | TLS cipher padding etc
    TLS record hdr | WebSocket frame hdr | WebSocket msg2 payload | TLS cipher padding etc
    TLS record hdr | WebSocket frame hdr | WebSocket msg3 payload | TLS cipher padding etc
    ...


When running plain TCP

  • all tested browsers will produce a new TCP segment on each and every WebSocket message sent
  • Autobahn|Python will send out multiple WebSocket messages within one TCP segment

The browsers will send the 8 WebSocket message to the wire like this:

TCP/IP segment | WebSocket frame hdr | WebSocket msg1 payload
TCP/IP segment | WebSocket frame hdr | WebSocket msg2 payload
TCP/IP segment | WebSocket frame hdr | WebSocket msg3 payload
...

Autobahn|Python sends this:

TCP/IP segment |
    WebSocket frame hdr | WebSocket msg1 payload
    WebSocket frame hdr | WebSocket msg2 payload
    WebSocket frame hdr | WebSocket msg3 payload
    ...


Optimization 1

From an efficiency point of view, the optimal way of sending multiple application level messages while reusing the message framing provided by WebSocket would be:

TCP/IP segment |
    TLS record hdr |
        WebSocket frame hdr | WebSocket msg1 payload
        WebSocket frame hdr | WebSocket msg2 payload
        WebSocket frame hdr | WebSocket msg3 payload
        ...
    | TLS cipher padding etc

However, this can only be done by changing the WebSocket implementation, which in the case of browsers isn't a viable option.

Optimization 2

If the application can easily do it's own message framing (as e.g. when using JSON or MsgPack), the optimal sending would be:

TCP/IP segment |
    TLS record hdr |
        WebSocket frame hdr |
            WebSocket msg1 payload
            WebSocket msg2 payload
            WebSocket msg3 payload
            ...
    | TLS cipher padding etc

This optimization can be done without any change to existing WebSocket implementations. The only requirement is that applications need an application message serialization format that allows for batching of multiple application level messages into one WebSocket message.

A simple scheme would work like this:

  1. Application messages are not sent directly as individual WebSocket messages (e.g. from JavaScript in browsers), but put into a queue.
  2. A worker sends out all application messages pending in the queue batched up into a single WebSocket message every 100ms or so.

The tradeoff is between (added) message latency (tuned via queue sender frequency) and wire-level efficiency.

Conclusions

1.   The overhead induced by TCP/IP and TLS in particular can dwarf the overhead of WebSocket itself.

2.   Applications can use application-level message batching to effectively reduce the overhead that can be induced by TCP/IP and TLS.

Measurements

Chrome 34 / TLS 1.2

Chrome 34: sending 8 WebSocket text messages of payload 13 characters in a loop over TLS 1.2:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over TLS 1.2:

Chrome 34 / plain TCP

Chrome 34: sending 8 WebSocket text messages of payload 13 characters in a loop over plain TCP:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over plain TCP:

Top

Firefox 28 / TLS 1.2

Firefox 28: sending 8 WebSocket text messages of payload 13 characters in a loop over TLS 1.2:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over TLS 1.2:

Firefox 28 / plain TCP

Firefox 28: sending 8 WebSocket text messages of payload 13 characters in a loop over plain TCP:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over plain TCP:

Firefox 26 / TLS 1.0

Firefox 26: sending 8 WebSocket text messages of payload 13 characters in a loop over TLS 1.0:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over TLS 1.0:

IE11 / TLS 1.2

IE11: sending 8 WebSocket text messages of payload 13 characters in a loop over TLS 1.2:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over TLS 1.2:

IE11 / plain TCP

IE11: sending 8 WebSocket text messages of payload 13 characters in a loop over plain TCP:

Top

Autobahn|Python: echo'ing 8 WebSocket text messages of payload 13 characters over plain TCP:

Start experimenting and prototyping for IoT applications with Crossbar.io and Raspberry Pi!

Loaded with all the software you need to make the board part of WAMP applications!



Learn more

Recent posts

Atom Feed

Search this Site

Stay Informed

Sign up for our newsletter to stay informed of new product releases and features:
Community Chat