Home > Software design >  Nodejs TCP server process packets in order
Nodejs TCP server process packets in order

Time:10-14

I am using a tcp server to recieve and process packets in Node.js. It should recieve 2 packets:

  • "create" for creating an object in a database. It first checks if the object already exists and then creates it. (-> takes some time process)
  • "update" for updating the newly created object in the database

For the sake of simplicity, we'll just assume the first step always takes longer than the second. (which is always true in my original code)

This is a MWE:

const net = require("net");

const server = net.createServer((conn) => {
  conn.on('data', async (data) => {
    console.log(`Instruction ${data} recieved`);
    await sleep(1000);
    console.log(`Instruction ${data} done`);
  });
});
server.listen(1234);
const client = net.createConnection(1234, 'localhost', async () => {
  client.write("create");
  await sleep(10); // just a cheap workaround to "force" sending 2 packets instead of one
  client.write("update");
});

// Just to make it easier to read
function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

If i run this code i get:

Instruction create recieved
Instruction update recieved
Instruction create done
Instruction update done

But i want the "create" instruction to block the conn.on('data', func) until the last callback returns asynchronously. The current code tries to update an entry before it is created in the database which is not ideal.

Is there an (elegant) way to achieve this? I suspect some kind of buffer which stores the data and a worker loop of some kind which processes the data? But how do i avoid running an infinite loop which blocks the event loop? (Event loop is the correct term, is it?)

Note: I have a lot more logic to handle fragmentation, etc. But this explains the issue i'm having.

CodePudding user response:

I managed to get it to work with the package async-fifo-queue. It's not the cleanest solution but it should do what i want and as efficient as possible (using async/await instead of just looping infinitely).

Code:

const net = require("net");
const afq = require("async-fifo-queue");

const q = new afq.Queue();

const server = net.createServer((conn) => {
  conn.on('data', q.put.bind(q));
});
server.listen(1234);
const client = net.createConnection(1234, 'localhost', async () => {
  client.write("create");
  await sleep(10);
  client.write("update");
});

(async () => {
  while(server.listening) {
    const data = await q.get();
    console.log(`Instruction ${data} recieved`);
    await sleep(1000);
    console.log(`Instruction ${data} done`);
  }
})();



function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

CodePudding user response:

You can pause the socket when you get the "create" event. After it finishes, you can resume the socket. Example:


const server = net.createServer((conn) => {
  conn.on('data', async (data) => {
    if (data === 'create') {
        conn.pause()
    }
    console.log(`Instruction ${data} recieved`);
    await sleep(1000);
    console.log(`Instruction ${data} done`);
    if (data === 'create') {
        conn.resume()
    }
  });
});
server.listen(1234);
const client = net.createConnection(1234, 'localhost', async () => {
  client.write("create");
  await sleep(10); // just a cheap workaround to "force" sending 2 packets instead of one
  client.write("update");
});

// Just to make it easier to read
function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

  • Related