0

Websocket on Client:

socket.send('helloworld');

Websocket on Node.js:

socket.ondata = function(d, start, end){
     // I suppose that the start and end indicates the location of the 
     // actual data 'hello world' after the headers
     var data = d.toString('utf8', start, end);
     
     // Then I'm just printing it
     console.log(data);
});

but I'm getting this: �����]���1���/�� on the terminal :O

I have tried to understand this doc: https://www.rfc-editor.org/rfc/rfc6455#section-5.2 but it's hard to understand because I don't know what should I work with, I mean I can't see the data even with toString?

I have tried to follow and test with this questions answer How can I send and receive WebSocket messages on the server side? but I can't get it to work, with this answer I was getting an array like this [false, true, false, false, true, true, false] etc... and I don't really know what to do with it.. :\

So I'm a bit confused, what the hell should I do after I get the data from the client side to get the real message?

I'm using the original client side and node.js API without any library.

Community
  • 1
  • 1
Adam Halasz
  • 57,421
  • 66
  • 149
  • 213
  • `I'm using the original client side and node.js API without any library.` why don't you just use the `socket.io` library? – Philipp Kyeck Feb 11 '12 at 08:56
  • 5
    @pkyeck because I would like to understand how websockets work :), I don't have any problem with socket.io, I already used it it's pretty cool, but I'm the type who wants to go deeper :) – Adam Halasz Feb 11 '12 at 16:46

4 Answers4

1

Which node.js library are you using? Judging by the fact that you are hooking socket.ondata that looks like the HTTP server API. WebSockets is not HTTP. It has an HTTP compatible handshake so that the WebSocket and HTTP service can live on the same port, but that's where the similarity ends. After the handshake, WebSockets is a framed full-duplex, long-lived message transport more similar to regular TCP sockets than to HTTP.

If you want to implement your own WebSocket server in node.js you are going to want to use the socket library directly (or build on/borrow existing WebSocket server code).

Here is a Node.js based WebSocket server that bridges from WebSocket to TCP sockets: https://github.com/kanaka/websockify/blob/master/other/websockify.js Note that it is for the previous Hixie version of the protocol (I haven't had opportunity or motivation to update it yet). The modern HyBI version of the protocol is very different but you might be able to glean some useful information from that implementation.

kanaka
  • 70,845
  • 23
  • 144
  • 140
  • 1
    Thanks very much for your answer, as I said I don't use any external libraries, just the original API's, the `socket.ondata` comes from the HTTP update event, `server.on('update', function(request, socket, head){ socket.ondata...})`. My problem is that I can't see the data as it writes in the websocket API docs, I don't know what I need to work with, I get it but in a very unreadable form, so I can't use any rules on it, because I don't see any scheme, so the problem is that I can't decode the data from the `socket.ondata`, if you could help me understand that part I would be really happy :) – Adam Halasz Feb 11 '12 at 22:16
  • @CIRK, to clarify, you cannot use the HTTP library for WebSocket because **WebSocket is not HTTP** (the initial handshake is similar but that's where the similarity ends). You have to work at a lower level (e.g. at the socket level). You might try referring to an existing working implementation like [faye-websocket-node](https://github.com/faye/faye-websocket-node) – kanaka Feb 13 '12 at 22:58
  • thanks, but what do you mean? Should I create a TCP server instead of an HTTP one? – Adam Halasz Feb 14 '12 at 00:37
  • @CIRK, yes, and you will need to parse the headers instead of relying on the HTTP server to have already done so when you get notified of a connection. – kanaka Feb 14 '12 at 05:01
  • I created a TCP server and it's the same, I can send messages but I can't decode them :)) – Adam Halasz Feb 14 '12 at 21:39
  • When you say you can send, are you seeing the message received by the browser's WebSocket onmessage handler? Are you XOR unmasking the payload from the client (before UTF-8 decode)? All payloads from browser to server are XOR masked and must be unmasked (server to browser is not masked). – kanaka Feb 14 '12 at 21:43
  • I have tried with this code http://chopapp.com/#k8oa2g6g, but at the end I get a bunch on numbers instead of the actual message. – Adam Halasz Feb 14 '12 at 21:47
  • What numbers are you getting back (at least the first few)? YOu may just need to convert them back to characters. – kanaka Feb 14 '12 at 22:08
  • I'm sending `hello world` from the client, and I'm getting `10410124714311132236140114108255` on the server after my decoding process. – Adam Halasz Feb 14 '12 at 22:25
  • I think you have a least a type coercion issue. E.g. the first two characters are: 104 is ASCII for 'h'. 101 is ASCII for 'e'. But it also looks like every 3rd and 4th character is corrupt somehow. – kanaka Feb 14 '12 at 23:02
  • but the numbers change every time I run the send command on the client, `155207166173156138189174129198174`, `109218246111061592378119211254`, `731981862327813116123583207178` – Adam Halasz Feb 14 '12 at 23:11
  • Hmmm I have changed the code a little bit to separate the numbers in the loop, and now it's a bit more accurate the values are changing but it has constant values too, some results: `104, 101, 121, 35, 111, 32, 98, 32, 114, 108, 113,`, `104, 101, 175, 227, 111, 32, 180, 224, 114, 108, 167,` – Adam Halasz Feb 14 '12 at 23:15
  • Yes, for some reason every 3rd and 4th character appears to be unstable. From the code you linked I don't see why that would be the case. – kanaka Feb 15 '12 at 15:03
1

You can in fact start with Node's HTTP API. That is exactly what I did when writing the WebSocket-Node module https://github.com/Worlize/WebSocket-Node

If you don't want to use an existing WebSocket Library (though you really should just use an existing library) then you need to be able to parse the binary data format defined by the RFC. It's very clear about the format and exactly how to interpret the data. From each frame you have to read in all the flags, interpret the frame size, possibly read the masking key, and unmask the contents as you read them from the wire.

That is one reason you're not seeing anything recognizable... in WebSockets, all client-to-server communications is obfuscated by applying a random mask to the contents using XOR as a security precaution against possibly poisoning the cache of older proxy servers that don't know about websockets.

Brian McKelvey
  • 853
  • 1
  • 8
  • 12
0

Here is a solution from this post:

https://medium.com/hackernoon/implementing-a-websocket-server-with-node-js-d9b78ec5ffa8

parseMessage(buffer) {
    const firstByte = buffer.readUInt8(0);
    //const isFinalFrame = Boolean((firstByte >>> 7) & 0x1); 
    //const [reserved1, reserved2, reserved3] = [ Boolean((firstByte >>> 6) & 0x1), 
    Boolean((firstByte >>> 5) & 0x1), Boolean((firstByte >>> 4) & 0x1) ]; 
    const opCode = firstByte & 0xF; 
    // We can return null to signify that this is a connection termination frame 
    if (opCode === 0x8) 
        return null; 
    // We only care about text frames from this point onward 
    if (opCode !== 0x1) 
        return; 
    const secondByte = buffer.readUInt8(1); 
    const isMasked = Boolean((secondByte >>> 7) & 0x1); 
    // Keep track of our current position as we advance through the buffer 
    let currentOffset = 2; let payloadLength = secondByte & 0x7F; 
    if (payloadLength > 125) { 
        if (payloadLength === 126) { 
            payloadLength = buffer.readUInt16BE(currentOffset); 
            currentOffset += 2; 
        } else { 
            // 127 
            // If this has a value, the frame size is ridiculously huge! 
            //const leftPart = buffer.readUInt32BE(currentOffset); 
            //const rightPart = buffer.readUInt32BE(currentOffset += 4); 
            // Honestly, if the frame length requires 64 bits, you're probably doing it wrong. 
            // In Node.js you'll require the BigInt type, or a special library to handle this. 
            throw new Error('Large payloads not currently implemented'); 
        } 
    }

    const data = Buffer.alloc(payloadLength);
    // Only unmask the data if the masking bit was set to 1
    if (isMasked) {
        let maskingKey = buffer.readUInt32BE(currentOffset);
        currentOffset += 4;

        // Loop through the source buffer one byte at a time, keeping track of which
        // byte in the masking key to use in the next XOR calculation
        for (let i = 0, j = 0; i < payloadLength; ++i, j = i % 4) {
            // Extract the correct byte mask from the masking key
            const shift = j == 3 ? 0 : (3 - j) << 3; 
            
            const mask = (shift == 0 ? maskingKey : (maskingKey >>> shift)) & 0xFF;
            // Read a byte from the source buffer 
            const source = buffer.readUInt8(currentOffset++); 
            // XOR the source byte and write the result to the data 
            data.writeUInt8(mask ^ source, i); 
        }
    } else {
        // Not masked - we can just read the data as-is
        buffer.copy(data, 0, currentOffset++);
    }

    return data 
}
c-toesca
  • 947
  • 1
  • 11
  • 13
0

There are two things -

  1. Which node.js version are you using? I have never seen a data event with start and endpt. The emitted event is just data with buffer/string as an argument.

  2. More importantly, if you are hooking on to the HTTP socket you should take care of the HTTP Packet. It contains hearders, body and a trailer. There might be garbage in there.

  • thanks @Satyam Shekhar, my node.js version is 6.0, well start and and is not empty, they are both numbers, I already tested it, but I just followed some tutorials/others works, that's why I use it in this way. I don't really understand your 2. point :\ – Adam Halasz Feb 13 '12 at 06:43
  • `ondata` is an internally used property... not something you want to use if you can help it. – Brad Feb 08 '15 at 02:39