Lesson 30. Working with Socket.Io

Building a web application in Node.js can be exciting. Often, you’ll find that the most challenging aspects stem primarily from architecting the application from a web-development perspective. It’s easy to forget what Node.js is capable of outside the normal request-response cycle. In this lesson, you explore communication between the client and server via an open TCP connection. This connection is made available by means of the socket.io package, which runs on web sockets and long polling, using normal HTTP requests held for longer periods on the server before responses are returned to facilitate a live-stream of data between client and server. You start by learning how to implement socket.io with Express.js. Then you create a chat box in a new application view. Last, you connect the client-side JavaScript and server code through custom events triggered and handled by socket.io.

This lesson covers

  • Implementing socket.io in a Node.js application
  • Structuring your socket.io listeners within a controller
  • Creating a simple chat feature

Consider this

You built a perfectly functional application with tons of users flocking to sign up. Unfortunately, these users have no way of communicating with one another. Given that you’re building an application that’s community-driven, communication among members is important. The user data is already in the database. All you need to do is associate that data through a tool that supports real-time communication.

With a little help from socket.io, you’ll soon be able to connect users so that they can chat with one another.

30.1. Using socket.io

You’ve built Node.js web applications that feature client-to-server communication. When the client wants to view a web page or post data, your application generates an HTTP request to the server. This method of communication over the internet has been around for a long time, celebrating its 20th birthday in 2017. In technology years, that’s old. Although developers still heavily rely on the request-response cycle, it isn’t the most effective method of communication for every use case.

What if you want to view the scores of an NBA basketball game in real time, for example? You can load the page containing scores and statistics, but you’d need to reload the page every time you wanted to see an update in information. For a basketball game, these changes can come as rapidly as every second. Repeatedly creating GET requests to the server is a lot of work to expect from the client. Polling is used to generate repeated requests from the client to the server in anticipation of updated server data. Polling uses the standard techniques you’ve used so far to transfer data between the client and server, but it sends requests so frequently that it creates an illusion of an open channel of communication between both participants (figure 30.1).

Figure 30.1. Polling between a client and server

To further improve on this technique, long polling was developed to reduce the number of requests needed to get updated data. Long polling behaves similarly to polling, in that the client makes repeated requests to the server for updated data, but fewer requests are made. Instead of making hundreds of requests when only dozens of them receive updated data, long polling allows requests to stay open as long as HTTP allows before the requests time out. Within that time—say, 10 seconds—the server can hold on to the request and either respond with updated data when the server receives it or respond with no changes before the request times out. This more-efficient approach has allowed web browsers and devices to experience a sense of real-time informational exchange over a protocol that hasn’t changed much for decades.

Although these two methods are widely used, a recent new addition has allowed platforms like Node.js to thrive. Web sockets were introduced in 2011 to allow an open stream of communication between clients and server, creating a true open channel that allows information to flow in either direction as long as the server or clients are available. Web sockets use a different internet protocol from HTTP but are supported in use with a normal HTTP server. In most cases, a server running with web sockets enabled allows its open channels to be reached over the same application ports you’d use for a typical request-response exchange (figure 30.2).

Figure 30.2. Opening a web socket connection between a client and server

Although web sockets are a preferred method for live communication, they’re not supported by many older browsers and clients. This relatively new technology allows developers to build applications that stream data in real time, and you can incorporate it into your existing Node.js application: socket.io, a JavaScript library that uses web sockets when it can and polling where web sockets are unsupported.

socket.io is also a package that can be installed within a Node.js application, providing library support for web sockets. It uses the event-driven communication in Node.js and web sockets to allow the client and server to send data by triggering events. As a client looking for updated basketball-game statistics, for example, you might have client-side JavaScript listening for an updated data event triggered by the server. Then your browser would handle the updated data event along with any data passed with it to modify the contents of your web page. These events can come in a continuous stream or hours apart, if needed. If you wanted to signal to the server to send a message to all other listening clients, you could trigger an event that the server knows how to handle. Luckily, you have control of both the client-side and server-side code, so you can implement the firing and handling of any events you want.

To start, install socket.io in your recipe application by running npm i socket.io -S in your project’s terminal window. You’ll use this library in the following sections to build a live-chat feature for users to communicate.

Quick check 30.1

Q1:

How is long polling different from polling?

QC 30.1 answer

1:

Long polling works by sending the server requests that are sustained longer than typical requests. Polling depends on many individual GET requests. Long polling is more efficient because it keeps a single GET request alive for a longer period, allowing the server to receive updates and respond before the client makes another request.

 

30.2. Creating a chat box

To get started with a chat feature, you need to build a basic view with a chat box and submit button. As you build the code to allow the client to handle server events, this chat box will populate with data.

Create a new view called chat.ejs in your views folder. Within this view, add the code in listing 30.1. In this code, you have a form that takes an input and a submit button. Below the form code is the tag created for the chat box. With some simple CSS styling, you can add a border and size dimensions to the chat box, prompting users to type the form input and submit it to add the content to the chat window below.

Listing 30.1. Creating a chat box in chat.ejs
<div class="container">
  <h1>Chat</h1>
  <form id="chatForm">                       1
    <input id="chat-input" type="text">      2
    <input type="submit" value="Send">
  </form>
  <div id="chat"></div>                      3
</div>

  • 1 Add an HTML form for chat input.
  • 2 Add a custom input element for chat content.
  • 3 Create a tag for the chat box.

To load this view, add a new route and action. Add router.get("/chat", homeController .chat) to homeRoutes.js in your routes folder. This new route will be absorbed by the index.js route file and used by main.js. Now you need to create the chat action in homeController.js, as shown in the next listing. In this action, you simply render the chat.ejs view.

Listing 30.2. Adding a chat action in homeController.js
chat: (req, res) => {
  res.render("chat");          1
}

  • 1 Render a chat view.

Relaunch your application, and visit http://localhost:3000/chat to see the chat box shown in figure 30.3.

Figure 30.3. Displaying chat view

Note

Your chat page will not look exactly like figure 30.3 unless you add custom styling to it.

With this chat page set up, you need to remember the tag IDs that you used in the HTML. In the next section, you target the #chat box with chat messages and send new messages found in #chat-input to the server.

Quick check 30.2

Q1:

Why does the HTML element with ID chat not have any content?

QC 30.2 answer

1:

The #chat element starts empty on each page load. You’ll use client-side Java-Script to populate the element with content as it’s received by the server.

 

30.3. Connecting the server and client

Now that you have a chat page, you need the guts to get it working. With socket.io installed, you need to require it into your project. Because you want your socket server to run on your existing Express.js HTTP server, require socket.io, and pass it to your Express.js server. Add the require line to main.js below the line where you tell your app to listen on a specified port, as shown in listing 30.3. In this code, you save the running server instance into a constant server so that you can pass the same Express.js HTTP server to socket.io. This process allows socket.io (which I’ll refer to as io) to attach to your application server.

Listing 30.3. Adding the server io object in main.js
const server = app.listen(app.get("port"), () => {
    console.log(`Server running at http://localhost:
 ${ app.get("port") }`);
  }),                                      1
  io = require("socket.io")(server);       2

  • 1 Save the server instance to server.
  • 2 Pass the server instance to socket.io.

Now you can start using io to build out your socket logic. As with your other code, though, compartmentalize this code into its own controller. Create a new chatController .js in your controllers folder, and require it below where you required socket.io. To require the controller, add require("./controllers/chatController")(io) to main.js. In this line, you’re passing the io object to your chat controller so that you can manage your socket connections from there. You don’t need to store this module in a constant because you won’t be using it further in main.js, so you can require it.

Note

It’s important that you require chatController.js after defining the io object. Otherwise, you won’t have socket.io configured for use in your controller.

Within chatController.js, add the code in listing 30.4. In this code block, you’re exporting all the controller’s contents and taking a single parameter: the io object from main.js. In this file, you use io to listen for certain events. To start, io listens for the connection event, indicating that a client has connected to the socket channel. In handling this event, you can use the specific client socket to listen for when the user disconnects or for custom events, such as the message event you created. If the server receives a message event, it uses io to send a string of data to all connected clients, using its emit method.

Listing 30.4. Handling chat socket connections in chatController.js
module.exports = io => {                     1
  io.on("connection", client => {            2
    console.log("new connection");

    client.on("disconnect", () => {          3
      console.log("user disconnected");
    });

    client.on("message", () => {             4
      io.emit("message", {
        content: "Hello"
      });                                    5
    });
  });
};

  • 1 Export the chat controller contents.
  • 2 Listen for new user connections.
  • 3 Listen for when the user disconnects.
  • 4 Listen for a custom message event.
  • 5 Broadcast a message to all connected users.
Note

Notice that you’re using the argument name client because this code will run with each new client connect. client represents the connected entity on the other side of the socket with the server. Client listeners run only if an initial io connection is made.

With this code in place, you need to set up the client-side code to handle data from and send events to the server. To accomplish this task, add some code to your recipeApp.js JavaScript code in your public folder.

In this code, initialize socket.io on the client side, allowing your server to detect that a new user has connected. Then, using jQuery, handle the form submission by emitting a message event to the server, and prevent the form from submitting naturally with return false. socket.emit takes a string argument as the event name and emits the event back to the server. Using socket.on, you listen for the message from the server, along with a string message. You display that message by appending it as a list item in your #chat element. On the server, you’ve already set up a handler in chatController.js for the message event to send back the message content "Hello" to the client.

Listing 30.5. Adding client-side JavaScript for socket.io in recipeApp.js
const socket = io();                        1

$("#chatForm").submit(() => {               2
 socket.emit("message");
 $("#chat-input").val("");
 return false;
});

socket.on("message", (message) => {         3
  displayMessage(message.content);
});

let displayMessage = (message) => {         4
  $("#chat").prepend($("<li>").html(message));
};

  • 1 Initialize socket.io on the client.
  • 2 Emit an event when the form is submitted.
  • 3 Listen for an event, and populate the chat box.
  • 4 Display messages from the server in the chat box.

The last step is loading the socket.io library on the client by adding a script tag to the view on which the chat is generated. To simplify this task, add the tag to your layout file. In layout.ejs, add <script src="/socket.io/socket.io.js"></script> below your other script and link tags. This tag tells your Node.js application to find the socket.io library in your node_modules folder.

Relaunch your application, visit http://localhost:3000/chat, enter some text in the input box, and click Send. You should see "Hello" in your chat box (figure 30.4). A new line should appear with each new text submission.

Figure 30.4. Displaying text in the chat box

In lesson 31, you improve this chat to allow the application to save these messages to your database.

Quick check 30.3

Q1:

What does io.emit do?

QC 30.3 answer

1:

The io object controls much of the communication between the server and the client. emit allows io to send some specific data by triggering an event and notifying all connected client sockets.

 

Summary

In this lesson, you learned about socket.io and saw how to install it in a Node.js application. Then you created your first chat application by using web sockets over your Express.js server to facilitate event and data exchange between client and server. When this chat feature is installed, users can communicate with one another in real time. When a client refreshes the web page, however, the chat history is erased. What’s more, you have no indication of which user sent which message. In lesson 31, you create a new data model and associate user accounts so that message authors can be identified and chats can persist across user sessions.

Try this

With a chat feature implemented, try sending more meaningful data between the client and server. The message content allows all clients to see the same messages at the same time, but maybe you want to see more than the message itself. Try sending the date stamp showing when the message was sent to the server. Then, with client-side Java-Script, collect that date stamp and display it next to the message in the chat box.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset