UDP

UDP

Alongside TCP, UDPUser Datagram Protocol is the other most common communication protocol. It provides a simple and lightweight connectionless communication model. Being connectionless, a very minimal amount of protocol mechanisms come into play when sending packets. While checksums are still used for data integrity, UDP is favored in real-time applications, as it avoids the overhead of setting up a connection, error-checking, and retransmission delays.

The dgram module

The dgram module provides an implementation of UDP Datagram sockets. These sockets are required for UDP communication.

const dgram = require("dgram");

const server = dgram.createSocket("udp4");
const port = 3500;

server.on("message", (data, rinfo) => {
  console.log(`Msg from client at port: ${rinfo.port}: ${data}`);
  server.send("Hello from server", rinfo.port, "localhost");
});

server.on("listening", function () {
  console.log("Server is listening on port", port);
});

server.on("close", function (err) {
  if (err) {
    console.log("Client disconnected due to error");
  } else {
    console.log("Client disconnected");
  }
  server.close();
});

server.bind(port);
Use ctrl + b, followed by a direction key, to switch panes in the terminal

The server-side

  • We import the dgram module on line 1.
  • The createSocket method on line 3 creates a UDP socket. A type must be passed; we are using udp4 in our case. The socket returned is an object of the EventEmitter class. This allows us to use the on method to handle different events.
  • Declaring our port early in the code and using the variable name will make changing it later on easier. We have set the port to 3500 on line 4.
  • The message event is fired when we receive a message from another socket. It returns the data and an rinfo object. We are outputting the data on line 7. rinfo has a lot of useful attributes; we use rinfo.port to send a message back to our client.
  • We send data on the socket by using the send method. We pass it a string of our choice, the port, and the IP addresslocalhost in our case of the recipient. This is done on line 8.
  • The listening event is fired when the socket object starts to listen for incoming connections. We are using this event to output a string to let us know that the server is up. This is done on line 11.
  • The close event is fired when the socket is fully closed. If the socket closes due to an error, an Error object is returned. We are handling this event on line 15.
  • Finally, to start the server, we use the bind() method. This fires the listening event we wrote earlier. It is passed a port number. Now our server is live and waiting for new connections.

The client-side

  • Our client-side looks very similar to the server-side since we are using the dgram.createSocket method again to make our socket.
  • As you can see, once our socket object is created, we can start sending data to our server right away. We do not need to connect to the server as we did in our TCP example. We are using the send method as we did in our server; however, here we are also checking for errors.
  • The only way to know for sure that the datagram has been sent is by using a callback. In case of an error, it will be passed as the first argument to the callback. If a callback is not given, the error is emitted as an error event on the socket object.
  • The data and end methods are used in a similar manner as we have used on the server-side. The end method is similar to the close method in terms of functionality.

Let’s make them talk

Using the readline module, once again, we can achieve the same functionality as we did with our TCP system. Let’s see how that might look with UDP.

const dgram = require("dgram");
const readline = require("readline");

const server = dgram.createSocket("udp4");
const port = 3500;

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: "",
});

let clientSocket = 0;

server.on("message", (data, rinfo) => {
  console.log(`Msg from client at port: ${rinfo.port}: ${data}`);
  clientSocket = rinfo.port;
});

server.on("listening", function () {
  console.log("Server is listening on port", port);
});

server.on("close", function (err) {
  if (err) {
    console.log("Client disconnected due to error");
  } else {
    console.log("Client disconnected");
  }
  server.close();
});

server.bind(port);

rl.prompt();
rl.on("line", function (line) {
  server.send(line, clientSocket, "localhost");
  rl.prompt();
});
Use ctrl + b, followed by a direction key, to switch panes in the terminal

The server-side

  • We import the readline module on line 2.
  • We use the createInterface method to use our streams.
  • We define a variable clientSocket on line 13. This is initially set to 0. We will set this to the port number that we use to communicate with the client once it connects.
  • Once the client connects, we assign clientSocket the value of rinfo.port on line 17.
  • We use the on method to add a listener for the line event. However, we use the send method instead of the write method we used for TCP. When sending on the socket, we use the clientSocket variable we set earlier.

The client-side

We create an interface and use the same methods as we used in server.js to send text from the console to the server.

Repetition?

This lesson may seem very similar to the previous lesson. You might be wondering if it was the same lesson again. Both TCP and UDP can be implemented in a similar way in Node.js. We did this so you can better understand the slight differences between the two. TCP takes a connection-oriented approach and ensures that packets are sent and received in order, unlike UDP, which takes a connectionless approach, offering no guarantee that the sent packets will be received on the other end. UDP, however, is often faster than TCP and is used in services like video calls and online gaming, where it is necessary to send packets as soon as possible, even if a few are dropped along the way.

svg viewer

Get hands-on with 1300+ tech skills courses.