How to write a TCP chat server with Deno?
Asked Answered
A

2

11

One of the demos that really convinced me of the power of Node was the simple TCP chat server that Ryan Dahl presented in this video: https://www.youtube.com/watch?v=jo_B4LTHi3I&t=28m23s

Here's what the code in the demo looked like:

const net = require('net');
const server = net.createServer();

const sockets = [];

server.on('connection', (socket) => {
  sockets.push(socket);

  socket.on('data', (message) => {
    for (const current_socket of sockets) {
      if (current_socket !== socket) {
        current_socket.write(message);
      }
    }
  });

  socket.on('end', () => {
    const index = sockets.indexOf(socket);
    sockets.splice(index, 1);
  });
});

server.listen(8000, () => console.log('tcp server listening on port 8000'));

The only TCP example I found on the Deno website is an echo server that looks like this:

const listener = Deno.listen({ port: 8080 });
console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  Deno.copy(conn, conn);
}

It's nice and compact, but I haven't been able to use Deno.Conn's read and write methods to turn this example into a TCP chat server. Any help would be much appreciated! I also think it would be a useful example to add to the website.

Angele answered 16/5, 2020 at 16:47 Comment(0)
I
9

Use Deno.listen to create the server and Deno.connect to connect to that server.

A simple example of tcp server/client would be:

server.js

const encoder = new TextEncoder();
const decoder = new TextDecoder();

const listener = Deno.listen({ port: 8080 });

console.log("listening on 0.0.0.0:8080");
for await (const conn of listener) {
  // Read message
  const buf = new Uint8Array(1024);
  await conn.read(buf);
  console.log('Server - received:', decoder.decode(buf))
  // Respond
  await conn.write(encoder.encode('pong'))
  conn.close();
}

client.js

const encoder = new TextEncoder();
const decoder = new TextDecoder();

const conn = await Deno.connect({ hostname: "127.0.0.1", port: 8080 })
// Write to the server
await conn.write(encoder.encode('ping'));
// Read response
const buf = new Uint8Array(1024);
await conn.read(buf);
console.log('Client - Response:', decoder.decode(buf))
conn.close();

You can build from here. For a chat server, you'll keep the connection open, and send multiple messages for example.

Ingaborg answered 16/5, 2020 at 17:0 Comment(1)
Thanks, I appreciate it! That's close to my solution :)Nitride
A
4

Alright, after more playing around, here's my TCP chat server:

const server = Deno.listen({ port: 8000 });
console.log("tcp server listening on port 8000");

const connections: Deno.Conn[] = [];

for await (const connection of server) {
  // new connection
  connections.push(connection);
  handle_connection(connection);
}

async function handle_connection(connection: Deno.Conn) {
  let buffer = new Uint8Array(1024);
  while (true) {
    const count = await connection.read(buffer);
    if (!count) {
      // connection closed
      const index = connections.indexOf(connection);
      connections.splice(index, 1);
      break;
    } else {
      // message received
      let message = buffer.subarray(0, count);
      for (const current_connection of connections) {
        if (current_connection !== connection) {
          await current_connection.write(message);
        }
      }
    }
  }
}

The code looks quite different from the Node version. That said, TCP does not maintain message boundaries and the Deno version makes that explicit by reading into a Uint8Array buffer. That's similar to how Rust's std::net and tokio::net modules handle TCP. Actually, I'm not too sure what the socket.on('data') events represent in Node; it seems like just an arbitrary-length piece of data from the TCP stream.

Angele answered 16/5, 2020 at 17:20 Comment(1)
socket.on('data') in node.js represents whatever data the OS provided on that TCP socket. TCP is a stream so what actual data arrives in what chunk is completely arbitrary. It is influenced by a lot of things including how it was sent and the transport in between, but not controlled by anyone. So, data arrives on a TCP socket in an arbitrary chunk of data, no matter the platform or environment unless you have higher level code that parses the data and turns it into specific blocks of data with semantic meaning (such as a message, a line, etc...).Bunk

© 2022 - 2024 — McMap. All rights reserved.