欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能

来源: 开发者 投稿于  被查看 19157 次 评论:181

简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能


前言

这篇文章实话说我有点虚,因为平时都不怎么研究这一块的,然后涉及到的知识点超多,我只能到处看看资料总结一下相关信息,所以在此我只想说句:
本文章内容只代表个人立场,有错必改!
原本打算一次性总结,后来越扯越多超过字数限制了,就干脆做成http系列文章了,不定时更新原有内容(发现哪里出错的话),不定时新增系列文章,请见谅!

因为之前写得太臃肿又不够详细,最近刚好复习到这一块的内容,所以决定把这些文章都拆分成更加细致一点,补充详细内容,优化排版布局,目前来看还是应该的,因为自身时间问题和平台编译的问题迟迟未改,只好等都改完之后才发出来。
网络协议基础 — TCP、UDP、SOCKET与参考模型
Http协议系列 — 协议原理构成与连接管理
Http协议系列 — cookie,Session,缓存机制
Http协议系列 — Http2、特殊字符编码与常见问题简答
Http协议系列—-进阶Https基础
WebSocket协议入门基础
简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能

修改

2018/08/10 转简体字,重新排版,内容不变

以下全部代码可以看sockit-chat-demo,个人环境为
日期: 2018/06/25
Chrome极速版: 版本号 2.0.6.38
nodejs: v10.2.0
boostrap4
socket.io2

界面实现

我们直接使用boostrap4实现一个简单界面效果,不要再细节花费多余的功夫.组件里有个List group可以将就拿来实现,另外底部固定一个输入框完成.因为不需要用到插件,只引入样式即可
Bootstrap4
user.html

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
    <meta charset="utf-8">
    <title></title>
    <link rel="stylesheet" href="../bootstrap.min.css">
    <style media="screen">
        html,
        body {
            height: 100%;
        }

        .submitWrappre {
            position: fixed;
            left: 0;
            right: 0;
            bottom: 0;
            border: 1px solid #999;
        }
    </style>
</head>

<body>
    <div class="list-group">
        <a class="list-group-item list-group-item-action flex-column align-items-start active">
            <div class="d-flex w-100 justify-content-between">
                <h5 class="mb-1">List group item heading</h5>
                <small>3 days ago</small>
            </div>
            <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p>
            <small>Donec id elit non mi porta.</small>
        </a>
        //为了间隔开
        <a class="list-group-item list-group-item-action flex-column align-items-start">
            <div class="d-flex w-100 justify-content-between">
                <h5 class="mb-1">List group item heading</h5>
                <small class="text-muted">3 days ago</small>
            </div>
            <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p>
            <small class="text-muted">Donec id elit non mi porta.</small>
        </a>
    </div>
    //为了间隔开
    <form class="submitWrappre">
        <div class="form-group">
            <textarea class="form-control" id="exampleFormControlTextarea1" rows="2"></textarea>
            <button type="button" class="btn btn-primary btn-sm" style="float:right">Small button</button>
        </div>
    </form>
</body>

</html>

以上代码可以看sockit-chat-demo的lesson1.

Socket.io

Socket.IO enables real-time bidirectional event-based communication.
It works on every platform, browser or device, focusing equally on reliability and speed.

SOCKET.IO 2.0
socket.io具有实时双向基于事件通讯,能兼容不同平台,浏览器或设备,同样关注可靠性和速度.
我们可以直接在浏览器和服务器都用socket.io库使用websocket通信.使用方法很简单,文档已经很详细了,就不复述了.

同时在同目录下新建一个server.js脚本,我们用nodejs创建一个服务器,先安装依赖socket.io和path.

yarn add socket.io path
//OR
npm i -D socket.io path

path.join方法将多个参数值字符串结合成一个绝对路径字符串,我们需要利用它简单判断请求资源路径

req.url === '/' ? './user.html' : (path.join(__dirname, '../', req.url));
// ./user.html同层html
// C:\project\sockit-chat\xx其他上层资源

我们创建一个服务器并且监听80端口,简单判断请求资源在当前目录下相对路径返回给前端.

var app = require('http').createServer(handler);
var fs = require('fs');
var path = require("path");

//为了间隔开
app.listen(80);

//为了间隔开
function handler(req, res) {
    var _path = req.url === '/'
        ? './user.html'
        : (path.join(__dirname, '../', req.url));

    //为了间隔开
    fs.readFile(_path, function(err, data) {
        if (err) {
            res.writeHead(500);
            return res.end('Error loading index.html');
        }

        res.writeHead(200);
        res.end(data);
    });
}

然后引入socket.io库并且监听my other event事件,同时触发news事件传播信息给前端.

var io = require('socket.io')(app);
io.on('connection', function(socket) {
    socket.emit('news', {hello: 'world'});
    socket.on('my other event', function(data) {
        console.log(data);
    });
});

前端写法一样
user.html

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.dev.js" charset="utf-8"></script>
<script type="text/javascript">
    var socket = io('http://localhost');
    socket.on('news', function(data) {
        console.log(data);
        socket.emit('my other event', {
            my: 'data'
        });
    });
</script>

以上代码可以看sockit-chat-demo的lesson2.

在当前目录下打开命令行,输入node server,启动服务器之后浏览器打开http://localhost/(默认端口80可省略).看到命令行打印{ my: 'data' },浏览器控制台打印Object {hello: "world"}即为成功.

界面响应

我们现在已经有了界面和通信实现了,现在先把界面响应实现出来.
1, 列表简化,把不需要的结构删除,只保留用户身份,时间,内容即可.
2, 输入框填写内容,点击按键触发通信同时输入框清空.
3, 不管是发送还是接收都要在列表末尾添加进去内容,通过.active区分用户身份.

<div class="list-group">
    <a class="list-group-item list-group-item-action flex-column align-items-start active">
        <div class="d-flex w-100 justify-content-between">
            <h5 class="mb-1">Me</h5>
            <small>3 days ago</small>
        </div>
        <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p>
    </a>
    //为了间隔开
    <a class="list-group-item list-group-item-action flex-column align-items-start">
        <div class="d-flex w-100 justify-content-between">
            <h5 class="mb-1">Other</h5>
            <small class="text-muted">3 days ago</small>
        </div>
        <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p>
    </a>
</div>
var socket = io('http://localhost'),
  $content = document.getElementById('exampleFormControlTextarea1'),
  $send = document.getElementById('send');

//为了间隔开
$send.onclick = function() {
  socket.emit('user_send', {
    content: $content.value
  });
  $content.value = '';
}

这里我用了ES6的模板写法,如果你们浏览器不支持就乖乖用原生写法吧

/**
 * [addMsg description]
 * @param {[bol]} identity [身份,1本人,0非本人]
 * @param {[type]} val      [description]
 */
function addMsg(identity, val) {
  var node = document.createElement("a"),
    str =
    `<a class="list-group-item list-group-item-action flex-column align-items-start ${identity ? 'active' : ''}">
        <div class="d-flex w-100 justify-content-between">
            <h5 class="mb-1">${identity ? 'Me' : 'Other'}</h5>
            <small class="text-muted">${new Date().toLocaleString()}</small>
        </div>
        <p class="mb-1">${val}</p>
    </a>`;
  node.innerHTML = str;
  $list.appendChild(node);
}

以上代码可以看sockit-chat-demo的lesson3.

聊天实现

1, 先把列表界面清除.只保留容器;
2, 定义前端发送给服务器的事件名称为user_send,保存在对象content属性;
3, 定义服务器发送给前端的事件名称为user_receive,保存在对象content属性;
user.html

socket.on('user_receive', function(data) {
  console.log('user_receive: ' + data.content);
  addMsg(0, data.content);
});

//为了间隔开
$send.onclick = function() {
  socket.emit('user_send', {
    content: $content.value
  });
  addMsg(1, $content.value);
  $content.value = '';
}

server.js
每个连接都有一个单独的socket对象,我们创建一个user_list保存每个socket对象,当我们接收到一条信息就遍历发送给其他的socket对象达到聊天效果.

const user_list = [];
io.on('connection', (socket) => {
  if (!user_list.includes(socket))  user_list.push(socket);
  socket.on('user_send', (data) => {
    user_list.map(item => {
      if (item !== socket) 
        item.emit('user_receive', {content: data.content})
    })
  });
});

以上代码可以看sockit-chat-demo的lesson4.

在当前目录下打开命令行,输入node server,启动服务器之后浏览器打开多个http://localhost/页面,然后随便在输入框填写东西,你就会发现其他页面也会自动接收相应数据了.

最终完成代码

user.html

<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">
  <title></title>
  <link rel="stylesheet" href="../bootstrap.min.css">
  <style media="screen">
    html,
    body {
      height: 100%;
    }

    .submitWrappre {
      position: fixed;
      left: 0;
      right: 0;
      bottom: 0;
      border: 1px solid #999;
    }
  </style>
</head>

<body>
  <div class="list-group"></div>

    //为了间隔开
  <form class="submitWrappre">
    <div class="form-group">
      <textarea class="form-control" id="exampleFormControlTextarea1" rows="2"></textarea>
      <button type="button" class="btn btn-primary btn-sm" style="float:right" id="send">Small button</button>
    </div>
  </form>

    //为了间隔开
  <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.dev.js" charset="utf-8"></script>
  <script type="text/javascript">
    var socket = io('http://localhost'),
      $content = document.getElementById('exampleFormControlTextarea1'),
      $send = document.getElementById('send'),
      $list = document.getElementsByClassName('list-group')[0];

    //为了间隔开
    socket.on('user_receive', function(data) {
      console.log('user_receive: ' + data.content);
      addMsg(0, data.content);
    });

    //为了间隔开
    $send.onclick = function() {
      socket.emit('user_send', {
        content: $content.value
      });
      addMsg(1, $content.value);
      $content.value = '';
    }
    //为了间隔开
    /**
     * [addMsg description]
     * @param {[bol]} identity [身份,1本人,0非本人]
     * @param {[type]} val      [description]
     */
    function addMsg(identity, val) {
      var node = document.createElement("a"),
        str =
        `<a class="list-group-item list-group-item-action flex-column align-items-start ${identity "code" >const app = require('http').createServer(handler),
  io = require('socket.io')(app),
  fs = require('fs'),
  path = require("path");

//为了间隔开
app.listen(80);

//为了间隔开
function handler(req, res) {
  const _path = req.url === '/'
    ? './user.html'
    : (path.join(__dirname, '../', req.url))

  fs.readFile(_path, (err, data) => {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }

    res.writeHead(200);
    res.end(data);
  });
}

//为了间隔开
const user_list = [];
io.on('connection', (socket) => {
  if (!user_list.includes(socket))  user_list.push(socket);
  socket.on('user_send', (data) => {
    user_list.map(item => {
      if (item !== socket)
        item.emit('user_receive', {content: data.content})
    })
  });
});

后话

主要思路已经交代完了,全文下来其实还是很简单的,特别是socket.io封装好了API,我们只需要傻瓜式使用即可,实际开发我们还有很多步骤需要做的,例如:

  • 注册登录唯一账号密码并且需要验证;
  • 登录数据保存数据库,nodejs需要连接数据库,支持多种数据库使用;
  • 针对特定账号发送信息;
  • 支持表情图;
  • 微信的撤回删除等操作;
    总之可以优化的地方很多,你们可以自行研究.

用户评论