基于Vue+Nodejs+Socket+Express的在线聊天室

Rain Liu 2022-01-17 11:47:30

项目概述

即时通讯自1998年面世以来,极大的方便了人们的通信,特别是近几年的迅速发展,不管在PC端软件还是作为手机等移动端软件的热度,即时通讯软件一直是居高不下,甚至是常年霸占软件排行榜的榜首。得益于Web技术的迅速发展,网页即使通信系统应用也日益渐增,网页即使通信不同于聊天软件,它不需要客户端,直接在浏览器即可运行。

由于传统的网页聊天室采用Ajax短轮询通信技术,这种技术需要定时不断向服务端发送请求才能都得到更新的信息,而每次请求的请求头包含很少的信息却需要很长的时间去处理,这就导致了信息不及时,占用网络带宽和浪费服务器资源等问题。

本项目为了更好的结局传统的这些在线聊天室的问题,采用基与HTML5的WebSocket协议,结合Node.js来设计一个Web即使通信系统。使用WebSocket协议可以更好的实现客户端和服务端的双向通信,而相比较传统的实现方式延迟更低,性能更好。Node.js的优点是适用于高并发的场景,且适用于事实数据交互应用,用来搭建即时通讯系统的服务端是最好的选择。

需求分析

本系统主要包含以下功能:

用户端的主要功能:用户登录,用户列表,私聊,图片传输,文件传输

服务端主要功能:建立连接,断开连接,监听用户请求

系统功能模块如下图:

用例图:

 

概要设计:

1.用户登录流程图

 

2.用户列表流程图

 

3.私聊流程图

 

4.图片传输流程图

 

5.文件传输流程图

 

 

类图:  

 

数据库设计

用户表

字段名

类型

描述

Accounter

varchar

登录账号

Passwd

varchar

用户名

Name

varchar

昵称

Sex

varchar

性别

Age

varchar

年纪

Avatar_url

varchar

头像连接

Friengds

Varchar

好友账号

 

文件表

字段名

类型

描述

File_id

Varchar

文件描述符

File_lx

Int

文件类型 1-文件 2-图片

3-语言 4-视频

File_url

varchar

文件连接地址

File_size

int

文件大小(格式M)

 

 聊天记录表

字段名

类型

描述

From

Varchar

发件人

To

Varchar

收件人

Time

Data

发送时间

Tex

Varchar

文本内容

File_id

varchar

包含文件的描述符

设计模式

由于该项目由前端为vue.js开发的,故使用了vue.js的MVVM模型。

MVVM模型

MVVM是Model-View-ViewModel的缩写,它是一种基于前端开发的架构模式,其核心是提供对View和ViewModel的双向数据绑定,这使得ViewModel的状态改变可以自动传递给View,即所谓的数据双向绑定。

Vue.js 可以说是MVVM 架构的最佳实践,专注于 MVVM 中的 ViewModel,不仅做到了数据双向绑定,而且也是一款相对来比较轻量级的JS 库,API 简洁,很容易上手。Vue.js 是采用 Object.defineProperty 的 getter 和 setter,并结合观察者模式来实现数据绑定的。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

Observer 数据监听器:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者,内部采用Object.defineProperty的getter和setter来实现。

Compile 指令解析器:它的作用对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。

Watcher 订阅者:作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数。

Dep 消息订阅器:内部维护了一个数组,用来收集订阅者(Watcher),数据变动触发notify 函数,再调用订阅者的 update 方法。

 

从图中可以看出,当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用 Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行扫描和解析,初始化视图,并订阅Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。

 

当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。

项目演示

首先启动项目:

分别在两个浏览器打开打开http://127.0.0.1:3000即进入项目聊天室

源码分析

serve.js文件:

var express = require('express');

var fs = require('fs');

var app = express();

var WebSocket = require('ws');

var WebSocketServer = WebSocket.Server,

wss = new WebSocketServer({ port: 8181, host: '127.0.0.1'});

//wss = new WebSocket("ws://192.168.13.77:8181");

 

var uuid = require('node-uuid');

var clients = [];

 

// 定义模板引擎

app.engine('html', function (filePath, options, callback) {

    fs.readFile(filePath, function (err, content) {

        if (err) return callback(new Error(err));

 

        var rendered = content.toString().replace('#title#', '<title>'+ options.title +'</title>');

        return callback(null, rendered);

    })

});

 

// 指定视图所在的位置

app.set('views', './views');

// 注册模板引擎

app.set('view engine', 'html');

 

app.use(express.static('./asset'));

 

app.get('/', function (req, res) {

    res.render('index', { title: '聊天室'});

})

 

var server = app.listen(3000, '127.0.0.1', function () {

    var host = server.address().address;

    var port = server.address().port;

    console.log('Example app listening at http://%s:%s', host, port);

});


 

function wsSend(type, client_uuid, nickname, message) {

    for (var i = 0; i < clients.length; i++) {

        var clientSocket = clients[i].ws;

        if (clientSocket.readyState === WebSocket.OPEN) {

            clientSocket.send(JSON.stringify({

                "type": type,

                "id": client_uuid,

                "nickname": nickname,

                "message": message

            }));

        }

    }

}

var clientIndex = 1;

wss.on('connection', function(ws) {

    var client_uuid = uuid.v4();

    var nickname = "游客" + clientIndex;

    clientIndex += 1;

 

    clients.push({ "id": client_uuid, "ws": ws, "nickname": nickname });

    console.log('client [%s] connected', client_uuid);

    var connect_message = nickname + " 加入聊天室";

 

    wsSend("notification", client_uuid, nickname, connect_message);

    console.log('client [%s] connected', client_uuid);

   

    ws.on('message', function(message) {

        if (message.indexOf('/nick') === 0) {

            var nickname_array = message.split(' ');

            if (nickname_array.length >= 2) {

                var old_nickname = nickname;

                nickname = nickname_array[1];

                var nickname_message = "Client " + old_nickname + " changed to " + nickname;

                wsSend("nick_update", client_uuid, nickname, nickname_message);

            }

        } else {

            wsSend("message", client_uuid, nickname, message);

        }

    });

   var closeSocket = function(customMessage) {

        for (var i = 0; i < clients.length; i++) {

            if (clients[i].id == client_uuid) {

                var disconnect_message;

                if (customMessage) {

                    disconnect_message = customMessage;

                } else {

                    disconnect_message = nickname + " has disconnected";

                }

                wsSend("notification", client_uuid, nickname, disconnect_message);

                clients.splice(i, 1);

            }

        }

    };

    ws.on('close', function () {

        closeSocket();

    });

    process.on('SIGINT', function () {

        console.log("Closing things");

        closeSocket('Server has disconnected');

        process.exit();

    });

});

此文件代码为项目的总配置文件,规定了路由,地址,socket通信接口等。

不同的浏览器设备会根据登录时间不同,默认给用户注册唯一的用户名:

使用Socket进行网络通信,互发信息:

学号:NP+314(LRY)

 

 

 

 

...全文
242 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧