Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading socket connection responses in Node.js

I'm in the process of converting a PHP function to JS for use with Node.

The PHP function:

  • takes in a partially formed packet as an arg
  • finalizes the packet
  • creates a socket
  • sends the packet over the socket
  • Reads the response code from the server
  • Looks up the error code in an array
  • Sends the error code back to the caller

I have most of the function converted except for the bit that reads the server response.

Original PHP Function (Comments are my understanding of what the code does. May be incorrect)

    function serverInteractive($buf) { // $buf = partially formed packet
    $fp = fsockopen($ip, $port , $errno, $errstr, 5);
    $rs = '';
    if (!$fp) return $this -> fsockerror;

    $packet = pack("s", (strlen($buf)+2)).$buf; // Finalizes the packet
    fwrite($fp, $packet); // Sends the packet to the server.

            // ----- Read Server Response START -----//
    $len = unpack("v", fread($fp, 2));
    $rid = unpack("c", fread($fp, 1));
    for ($i = 0; $i < (($len[1] - 4) / 4); $i++) { 
        $read = unpack("i", fread($fp, 4));
        $rs .= $read[1];
    }
            // ----- Read Server Response FINISH -----//
    fclose($fp); // Closes the socket
    $result = $this -> socketerrors[$rs]; 
            // $socketerrors is an array of error messages.

return($result);
}

My JavaScript Version

var net = require('net');
var submitPacket = function(packet) {

    // Generate Final Packet
    var p = pack('s', packet.length + 2) + packet;

    // Create socket to Server
    var serverSocket = net.createConnection(config.serverConfig.port,
                                            config.serverConfig.host,
        function() {
         // Executes of connection is OK.
            console.log("Connected to Server");
            if (serverSocket.write(p, function() {
                console.log("Buffer Flushed!");
            })) {
                console.log("Packet sent ok!");
            } else {
                console.log("There was a problem sending the packet!")
            }
        });

    serverSocket.on('error', function(error) {
        if (error) {
            console.log(error);
        }
    });

    serverSocket.on('data', function(data) {
        if (data) {
            console.log("Response: ", data);

           // Need to put the error code generation
           // here and fire a callback.

            serverSocket.end();
        }
    });

}

The response i get from the server looks something like this when everything is ok:

<Buffer 07 00 06 01 00 00 00>

When not ok, looks something like this:

<Buffer 0b 00 06 00 00 00 00 03 00 00 00>

Any advice would be greatly appreciated.

UPDATE 1: This is what i've come up with so far however it the resulting code is undefined.

serverdSocket.on('data', function(data) {
        if (data) {
        var response = toArrayBuffer(data);
        var len = response.slice(0,2);
        var rid = response.slice(0,1);
        var rs = '';
        for (var i = 0; i < ((len[1]-4) / 4); i++) {
            var read = response.slice(0,4);
            rs += read[1];
        }
        console.log("Code: ",rs);
    }

UPDATE 2: The PHP unpack function does indeed convert a buffer of binary data into an array. It looks like i can do the same thing by JSON.stringify then JSON.parse() to get it into an array. I now have an array object of the correct data, but the rest of the function doesnt seem to replicate the original.

like image 708
Andrew Avatar asked Nov 01 '22 02:11

Andrew


1 Answers

I'll give it a try, although you haven't actually said what you want the "Code:" output to look like for the two input strings. We'll start with the differences between the PHP code and the JavaScript code.

Let's talk about these lines from the PHP code:

$len = unpack("v", fread($fp, 2));
$rid = unpack("c", fread($fp, 1));

Now those little fread() function calls are actually reading data from an input stream/file/whatever. The bottom line is that $len gets its value from the first two bytes of the stream and $rid gets its value from the third byte.

Now compare with the JavaScript code:

    var len = response.slice(0,2);
    var rid = response.slice(0,1);

I have a few observations about this code. First, the calls to slice() are both operating on the same array-like object, starting from the same location. So the value of rid will be wrong. Second, the value for rid is never used in the sample code, so if you never really need it you can eliminate that line entirely (which you could not do in the PHP code). The third observation is that calling slice() seems like overkill. Why not just use the square brackets on response?

One final puzzle about the original PHP code. Looking at the code that builds up $rs:

$rs .= $read[1];

It looks like this is a string concatenation of the successive integer data values. It's been a few years since I worked in PHP, so maybe I'm missing some subtlety, but this seems like a kind of odd thing to do. If you could tell me what the expected output codes are supposed to be it would help.

That brings me to the data itself. Just guessing from the data examples in the PHP code, it looks like the first two bytes are a little-endian encoded 16-bit buffer length. The next byte is the rid value, which is 6 in both examples. Then it looks like the rest of the buffer is composed of 32-bit little-endian values. Of course this is a WAG, but I'm just working with what has been provided.

So here's some code that processes the data values from the two sample buffers. For the first buffer it prints Code: 1 and for the second Code: 03. If these are not the correct values, then let me know what the expected output is and I'll see if I can make it work.

function toArrayBuffer(buffer) {
    var ab = new ArrayBuffer(buffer.length);
    var view = new Uint8Array(ab);
    for (var i = 0; i < buffer.length; ++i) {
        view[i] = buffer[i];
    }
    return ab;
}

function serverSocketOnData(data)
{
    if (data) {
        var response = toArrayBuffer(data);
        var len = response[0] + 256*response[1];
        var rid = response[2]; // get rid of this line?
        var rs = '';
        for (var i = 3; i < len; i+=4) {
            rs += response[i];
            // if the 32-bit value could ever exceed 255, then use this instead:
            // rs += response[i] + 256*response[i+1] +
            //       256*256*response[i+2] + 256*256*256*response[i+3];
        }
        console.log("Code: ",rs);
    }
}

function testArray(sourceArray)
{
    var sourceBuffer = new Buffer(sourceArray);
    console.log("source buffer");
    console.log(sourceBuffer);
    serverSocketOnData(sourceBuffer);
}

function main()
{
    var sourceArray1 = [0x07,0x00,0x06,0x01,0x00,0x00,0x00];
    var sourceArray2 = [0x0b,0x00,0x06,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00];

    testArray(sourceArray1);
    testArray(sourceArray2);
}
main();
like image 130
Lee Jenkins Avatar answered Nov 10 '22 00:11

Lee Jenkins