1

I found a post at https://stackoverflow.com/a/10402443/2755042 explaining how you can encode/frame and decode websocket data. I'm having troubles with the encoding part. I am able to successfully get data from the client and log it to the console, but when i turn around and re-encode that message and send it back to the client, I get an error:

net.js:614
throw new TypeError('invalid data');
      ^
TypeError: invalid data

Here is my code for encoding:

function encodeWebSocket(bytesRaw){
  var bytesFormatted = new Array();
  bytesFormatted[0] = 129;
  if (bytesRaw.length <= 125) {
    bytesFormatted[1] = bytesRaw.length;
  } else if (bytesRaw.length >= 126 && bytesRaw.length <= 65535) {
    bytesFormatted[1] = 126;
    bytesFormatted[2] = ( bytesRaw.length >> 8 ) & 255;
    bytesFormatted[3] = ( bytesRaw.length      ) & 255;
  } else {
    bytesFormatted[1] = 127;
    bytesFormatted[2] = ( bytesRaw.length >> 56 ) & 255;
    bytesFormatted[3] = ( bytesRaw.length >> 48 ) & 255;
    bytesFormatted[4] = ( bytesRaw.length >> 40 ) & 255;
    bytesFormatted[5] = ( bytesRaw.length >> 32 ) & 255;
    bytesFormatted[6] = ( bytesRaw.length >> 24 ) & 255;
    bytesFormatted[7] = ( bytesRaw.length >> 16 ) & 255;
    bytesFormatted[8] = ( bytesRaw.length >>  8 ) & 255;
    bytesFormatted[9] = ( bytesRaw.length       ) & 255;
  }
  for (var i = 0; i < bytesRaw.length; i++){
    bytesFormatted.push(bytesRaw.charCodeAt(i));
  }
  return bytesFormatted;
}

Here is the code that uses the encodeWebSocket function:

server.on('connection', function (socket) {
  socket.on('data', function (data) {
    // from client i send 'hello'
    var decodedMessage = (decodeWebSocket(data));
    console.log(decodedMessage); // hello
    console.log(typeof decodedMessage); // string
    var encodedMessage = encodeWebSocket(decodedMessage);
    socket.write(encodedMessage);
  });
});

Essentially, all I am trying to do is create a chat server that accepts a message, and turns around and sends it back to all other clients that are connected.

Any help is much appreciated.

Community
  • 1
  • 1
Cade W.
  • 369
  • 6
  • 14
  • I don't think you totally understand what the code above does. It basically looks like a bit packing function that allows you to send binary data. It sends text by default; most people send serialized JSON objects. – SamT Aug 07 '14 at 18:06
  • I agree with you that I don't totally understand what this function is doing. How might i go from having the `decodedMessage` and getting it in the proper format to send inside `socket.write()`? – Cade W. Aug 07 '14 at 18:13

2 Answers2

2

The code in Cade's answer is a reasonable start. But there are a few fine points that are addressed in the following code.

First, there is a possible difference in string length and corresponding buffer length when dealing with unicode. For example:

> s = 'a'
'a'
> s.length
1
> b = new Buffer(s, 'utf-8')
<Buffer 61>
> b.length
1

but:

> s = 'å'
'å'
> s.length
1
> b = new Buffer(s, 'utf-8')
<Buffer c3 a5>
> b.length
2

Therefore it is better to encode the data as a Buffer first, then use the length of the Buffer as the payload length.

Also, it's possible that the payload length is > 0xffff, in which eight bytes are needed for the payload length, instead of two.

Two other minor differences in the code below are the addition of an optional callback and consolidating the writes into a single call. While the writes might be buffered, they are potentially two system calls.

function send(data, encoding, callback) {
  var socket = this;
  var header;
  var payload = new Buffer(data, encoding);
  var len = payload.length;
  if (len <= 125) {
    header = new Buffer(2);
    header[1] = len;
  } else if (len <= 0xffff) {
    header = new Buffer(4);
    header[1] = 126;
    header[2] = (len >> 8) & 0xff;
    header[3] = len & 0xff;
  } else { /* 0xffff < len <= 2^63 */
    header = new Buffer(10);
    header[1] = 127;
    header[2] = (len >> 56) & 0xff;
    header[3] = (len >> 48) & 0xff;
    header[4] = (len >> 40) & 0xff;
    header[5] = (len >> 32) & 0xff;
    header[6] = (len >> 24) & 0xff;
    header[7] = (len >> 16) & 0xff;
    header[8] = (len >> 8) & 0xff;
    header[9] = len & 0xff;
  }
  header[0] = 0x81;
  socket.write(Buffer.concat([header, payload], header.length + payload.length), 'binary', callback);
}
Casey
  • 41
  • 6
1

After much searching, here is the code that i managed to get working to encode data to send to the client:

function send(msg) {
  // http://stackoverflow.com/questions/8214910/node-js-websocket-send-custom-data
  var socket = this;
  var newFrame = new Buffer(msg.length > 125 ? 4 : 2);
  newFrame[0] = 0x81;
  if (msg.length > 125) {
    newFrame[1] = 126;
    var length = msg.length;
    newFrame[2] = length >> 8;
    newFrame[3] = length & 0xFF;
  }
  else {
    newFrame[1] = msg.length;
  }
  socket.write(newFrame, 'binary');
  socket.write(msg, 'utf8');
}
Cade W.
  • 369
  • 6
  • 14