了解Socket.io的基础

本文最初发布在我的网站上。如果喜欢,您可能会在我的博客中找到有趣的先前文章

最近做了很多API和微服务后,我一直在尝试为快速演示找到新的想法来提高我的技能。在我正在从事的项目之一中,我计划包含一个提要,该提要随用户的活动实时更新。我不确定该怎么做,起初我考虑过使用RabbitMQ,但是经过快速搜索后,我发现了WebSockets和Socket.io。如果您想了解WebSockets是什么,请观看此超快速视频以了解基本概念。

我决定直接构建一个快速的聊天演示,而不是直接为我的项目构建用户的供稿。有多篇文章和视频介绍了如何创建聊天
 使用socket.io,但是其中大多数都没有完全解释所有涉及的部分
一起工作或只是在本地运行的小型演示,但
不能“部署”到生产中。因此,我将所有这些示例作为构建我的聊天室的参考,记录了我不了解的所有内容,并以一种可以将其部署到服务器(甚至创建了Docker映像)的方式进行构建。这是我所有的笔记。

聊天服务器和客户端职责

我们的聊天应用服务器将承担以下责任:

  • 为用户提供HTML / CSS / JS客户端文件
  • 启动Socket.io连接
  • 将socket.io库提供给客户端(可选,因为客户端也可以从CDN加载它)
  • 向所有连接的客户端广播事件(例如新的聊天消息)

当用户从浏览器连接到我们的服务器时,他将收到HTML / CSS / JS客户端文件,这些文件将:

  • 从我们的服务器或CDN加载socket.io客户端库
  • 与我们服务器中运行的Socket.io的稳定连接
  • 要求用户输入他的名字,以便我们在聊天中识别他
  • 向我们服务器中运行的Socket.io发送事件或从中接收事件
  • 通过JavaScript将我们自己的消息添加到聊天中

聊天服务器详细

第一件事是使用“ npm init”启动我们的Node.js项目,因为我们稍后必须安装依赖项。我们可以使用Node的http模块来创建一个静态服务器,该服务器向客户端发送任何类型的文件,在本例中为html,css和js。我在Mozilla文档中找到了这个示例,正是我想要的。没有框架,只有一个能够发送html,css,js,图像等内容的http服务器。他们还解释了它是如何逐行工作的,因此我不再赘述。我将服务器代码放在名为server.js的文件中。我从Mozilla示例中更改的唯一内容是端口号和读取文件的路径,因为我将使用名为“ client”的文件夹:

var filePath = './client' + request.url;
console.log(filePath)
if (filePath == './client/') {
  filePath = './client/index.html';
}

下一步是使用“ npm i socket.io”安装socket.io依赖项,并将其包含在我们的server.js文件中,并在检测到连接时记录一些内容:

var io = require('socket.io')(app);
// starts socket
io.on('connection', function (socket) {
  console.log('Socket.io started.....')
  // Manage all socket.io events next...
    socket.on('new-connection', (data) => {
    // captures event when new clients join
    console.log(`new-connection event received`)
    .........
  })
});

我还介绍了如何捕获名为“ new-connection”的事件,目前该事件仅会在控制台中打印出来。现在,让我们转到客户端。

详细聊天客户端

如前所述,我将所有客户端文件(html,css和js)放在一个名为client的文件夹中。 index.html文件非常简单:

  • 在标头中,我们从CDN加载socket.io客户端库,尽管我还包括了从我们自己的服务器加载它的代码
  • 同样在标题中,我们加载了script.js文件。
  • 正文仅包含所有聊天消息的div容器和提交新消息的表单。

在客户端script.js文件中,我所做的第一件事是通过socket.io从客户端连接到服务器。当我在script.js文件之前加载socket.io库时,我已经可以使用它了,因此我可以使用io()函数创建一个连接到服务器的套接字,并使用generate()函数发送一个名为“新连接”和用户名:

/**
 * Use io (loaded earlier) to connect with the socket instance running in your server. 
 * IMPORTANT! By default, socket.io() connects to the host that 
 * served the page, so we dont have to pass the server url
 */
var socket = io();

//prompt to ask user's name 
const name = prompt('Welcome! Please enter your name:')

// emit event to server with the user's name
socket.emit('new-connection', {username: name})

此时,如果我使用“ node server.js”启动服务器并打开浏览器,则得到提示,输入名称后,我将连接到套接字服务器,并在服务器控制台中看到类似以下内容:


$npm start

> chatsocket.io@1.0.0 start /d/Projects/chatSocket.io
> node server.js

HTTP Server running at http://127.0.0.1:3000/
request  /
./client/
request  /script.js
./client/script.js 
request  /style.css
./client/style.css
Socket.io started.....
request  /favicon.ico
./client/favicon.ico

到目前为止,我已经能够:

  • 启动发送客户端文件的静态服务器并打开socket.io连接
  • 通过socket.io将客户端连接到服务器,并发出一个名为“ new-connection”的事件
  • 捕获服务器中的“新连接”事件并将其打印到控制台

完成聊天应用程序缺少的唯一内容是:

  • 能够用用户名链接消息
  • 添加我们发送到chat-c​​ontainer div的消息
  • 向服务器发送包含已发送消息的事件
  • 将服务器中收到的聊天消息广播到所有连接的客户端

添加我们发送到chat-c​​ontainer div的消息是我们可以在客户端script.js文件中执行的操作。我们只需要添加一个事件侦听器来捕获提交表单时的内容,并且只要发生这种情况,就可以在聊天容器内创建一个包含消息的新div。因为这是当我们收到其他用户的消息时也必须做的事情,所以我创建了一个名为addMessage(data,type)的函数,该函数可以多次调用。另外,我触发了一个名为“ new-message”的事件,该事件向服务器发送一个带有消息和客户端套接字ID的对象。


// get elements of our html page
const chatContainer = document.getElementById('chat-container')
const messageInput = document.getElementById('messageInput')
const messageForm = document.getElementById('messageForm')

messageForm.addEventListener('submit', (e) => {
  // avoid submit the form and refresh the page
  e.preventDefault()
  // check if there is a message in the input
  if(messageInput.value !== ''){
    let newMessage = messageInput.value
    //sends message and our id to socket server
    socket.emit('new-message', {user: socket.id, message: newMessage})
    addMessage({message: newMessage}, 'my' )
    //resets input
    messageInput.value = ''
  }
})

// receives two params, the message and if it was sent by you
// so we can style them differently
function addMessage(data, type){
  const messageElement = document.createElement('div')
  messageElement.classList.add('message')

  if(type === 'my'){
    messageElement.classList.add('my-message')
    messageElement.innerText = `${data.message}`

  }else if(type === 'others'){
    messageElement.classList.add('others-message')
    messageElement.innerText = `${data.user}: ${data.message}`

  }else{
    messageElement.innerText = `${data.message}`

  }
  // adds the new div to the message container div
  chatContainer.append(messageElement)
}

请注意,我还为消息添加了不同的样式,具体取决于它们是属于用户还是从其他人接收到。

下一步是在我们的server.js中正确处理“新连接”和“新消息”事件。在“新连接”事件中,我将客户端的套接字ID和用户名存储为名为users的对象的key:values。然后在“新消息”事件中,我使用接收到的套接字ID查找对应的用户名,并通过broadcast()函数将消息信息发送给所有连接的客户端,但最初发送该事件的客户端除外。


// we'll store the users in this object as socketId: username
const users = {}

var io = require('socket.io')(app);
// starts socket
io.on('connection', function (socket) {
  console.log('Socket.io started.....')
  // Manage all socket.io events next...
  socket.on('new-connection', (data) => {
    console.log(`new-connection event ${data.username}`)
    // adds user to list
    users(socket.id) = data.username
    socket.emit('welcome', { user: data.username, message: `Welcome to this Socket.io chat ${data.username}` });
  })
  socket.on('new-message', (data) => {
    console.log(`new-message event ${data}`);
    // broadcast message to all sockets except the one that triggered the event
    socket.broadcast.emit('broadcast-message', {user: users(data.user), message: data.message})
  });
});

完成这最后几步后,我有了一个功能齐全的聊天应用程序,可以通过在本地打开多个浏览器进行测试:

聊天应用

到目前为止,我唯一没有涉及的就是样式(可以在style.css文件中找到)和较小的验证,例如确保用户不能发送空消息。您可以在GitHub的此仓库中找到完整的代码。它还包含一个Dockerfile,因此您可以使用Docker构建映像并将其部署到任何地方,或者如果您只想在线尝试,请访问此链接。

对于这个应用程序,我只需要使用emit()和broadcast()函数,但是Socket.io包含很多功能,例如为套接字分配名称空间,以便它们具有不同的端点,创建房间,甚至将其与Redis集成。您可以在文档中找到所有这些示例。

希望这可以帮助您了解WebSockets以及Socket.io的工作方式。

编码愉快

资讯来源:由0x资讯编译自DEV,原文:https://dev.to/uf4no/understanding-the-basics-of-socket-io-3a0e ,版权归作者所有,未经许可,不得转载
你可能还喜欢