标签: 课程设计

  • 物联网工程设计与实施实验

    要求

    一、安装部署分析开源物联网系统ThingsBoard。

    ThingsBoard安装软件及文档百度网盘:

    https://pan.baidu.com/share/init?surl=yOLXUmWXgyO9MYqDzd-aIw

    提取码:1234

    二、分析研究ThingsBoard在不同领域的应用。例如:

    1. 智慧农业监控系统;
    2. 智能家居安防系统;
    3. 物联网粮食仓储系统;
    4. 远程医疗系统:
    5. 智能停车系统;
    6. 智能无人超市系统;
    7. 文物(博物馆)预警监测系统;
    8. 平安校园系统;
    9. 智慧公交系统;
    10. 智能大厦系统等等。

    三、要求:

    1. 在上述应用领域中自选一题,基于ThingsBoard,从 需求分析、方案设计、工程实施 三方面着手,遵循 物联网工程规划与设计原则 ,实现一个较全面的物联网工程项目,撰写一份较完整的设计报告,最后要有自己对该设计的 体会与感悟 ;(也可着重进行某一方面的阐述)。一个项目不超过2人,且分 不同侧重点 独立成文。
    2. 最好10页( 20面左右 ,A4纸双面打印,带封面(包括课程名、班级、学号、姓名、任课老师、成绩等内容)。
    3. 上交时间为本课程结课时间。6.14,纸质档

    要求分析

    物联网工程规划与设计原则:

    1. 清晰的目标和用例定义:在开始物联网项目之前,明确定义项目的目标和用例非常重要。这有助于确定所需的功能和性能,并为后续的工程规划和设计提供指导。
    2. 弹性和可扩展性:考虑到物联网系统的增长和变化,设计具有弹性和可扩展性的架构是关键。系统应能够适应新设备的添加、增加处理能力和容量,并支持未来的功能扩展。
    3. 安全性和隐私保护:物联网系统需要具备安全性和隐私保护的能力。包括数据加密、身份验证和访问控制等安全措施,以确保数据的机密性和完整性,并保护用户的隐私。
    4. 设备和网络互操作性:物联网涉及多个设备和网络之间的通信和互操作性。确保设备和网络能够相互通信、交换数据,并支持不同的通信协议和标准是重要的。
    5. 数据管理和分析:物联网系统生成大量的数据,因此,合理的数据管理和分析策略至关重要。包括数据采集、存储、处理、分析和可视化等方面的设计,以从数据中提取有用的信息和洞察力。
    6. 设备生命周期管理:考虑到物联网设备的寿命周期,包括设备的部署、配置、监测、维护和更新等方面的管理是必要的。确保设备的可管理性和远程维护能力是物联网工程设计中的重要考虑因素。
    7. 故障恢复和容错能力:物联网系统需要具备故障恢复和容错能力,以应对设备故障、通信中断和其他问题。采用冗余设计、备份和恢复策略,以及监测和警报机制,可以帮助及时发现和解决问题。

    软件

    需要安装的一些软件。

    1. JDK(教程中说的是JDK11)
    2. Maven
    3. Node.js
    4. git
    5. idea
    6. PostgreSQL

    软件安装过程

    建议使用Docker

    好处是删的快,用完就删。

    安装文档

    步骤

    1. 安装 Docker Desktop 并运行
    2. 管理员终端运行 docker pull thingsboard/tb-postgres这是安装 ThingsBoard 镜像的命令。
    3. 在Docker Desktop下启动ThingsBoard,不需要多余的操作。
    4. containers里查看容器,大概如下图image-20230610165241946点 9090 端口,它映射到我电脑的 32768 端口上。
    5. 登录,密码如下
  • 数据库课程设计

    图太多了,俺老登不中了。PDF将就着看吧

  • Web 课程设计

    本文参考1 WebSocket之仿QQWeb即时聊天系统(上)

    本文参考2 WebSocket之仿QQWeb即时聊天系统(下)

    本文参考3 Node.js 教程_菜鸟

    本文参考4 socket.io 概述

    摇骰子摇到了1,所以选择了仿QQWeb即时聊天系统

    功能要求

    1. 实现Web的点对点即时的文本消息聊天功能。
    2. 实现Web的表情的发送、接收和显示功能。
    3. 实现Web的图片的发送、接收和显示功能。
    4. 实现本地消息的存储,在离线的时候也能加载和查看历史消息;
    5. 要求使用WebSocket

    功能分析

    就是能发 文本、表情、图片。点对点。

    还使用了WebSocket

    WebSocket

    WebSocket

    WebSocket 是独立的、创建在 TCP 上的协议。

    Websocket 通过HTTP/1.1 协议的101状态码进行握手。

    为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

    WebSocket的出现,使得浏览器具备了实时双向通信的能力。

    1. WebSocket可以在浏览器里使用
    2. 支持双向通信
    3. 使用很简单

    WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

    现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

    HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

    浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。

    当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

    WebSocket是一个很棒的概念,我单独拿出来写。

    WebSocket

    创建 对象

    以下 API 用于创建 WebSocket 对象。

    var Socket = new WebSocket(url, [protocol] );
    

    以上代码中的第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。


    属性

    以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

    属性描述
    Socket.readyState只读属性 readyState 表示连接状态,可以是以下值:0 – 表示连接尚未建立。1 – 表示连接已建立,可以进行通信。2 – 表示连接正在进行关闭。3 – 表示连接已经关闭或者连接不能打开。
    Socket.bufferedAmount只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

    WebSocket 事件

    以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

    事件事件处理程序描述
    openSocket.onopen连接建立时触发
    messageSocket.onmessage客户端接收服务端数据时触发
    errorSocket.onerror通信发生错误时触发
    closeSocket.onclose连接关闭时触发

    WebSocket 方法

    以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

    方法描述
    Socket.send()使用连接发送数据
    Socket.close()关闭连接

    为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。

    一些准备工作

    根据大佬RongLin02的博客做一些准备工作。

    首先是对于WebSocket的了解。以上已经介绍了。

    然后是安装Node.js,你也可以进入 官网 下载。学习页面。

    NPM的安装,在上一步极有可能已经附带完成。

    socket.io

    是一个WebSocket库,用起来很方便。

    库所以只需要引入而不需要下载。

    在项目根目录 右键–在集成终端中打开

    执行以下代码:

    npm install jquery
    npm install socketio
    npm install express
    

    会生成一个node_modules文件夹用来存放安装好的模块,一些配置信息会生成在package-lock.jsonpackage.json文件中。

    使用方法

    中文文档

    在中文文档里面发现第一个文档 socket.io 快速入门教程——聊天应用 就和我们的任务很像。

    按照其流程一步一步来。

    常用方法

    socket.emit('A',value);
    

    这个是发送方法,A是名字,叫什么名字都行,value就是要发送的值,可以是服务器给客户端发送,也可以是客户端给服务器发送,类型也可以是一个对象。

    socket.on('A',function(data){
            console.log(data);
        });
    

    有发送就有接受,服务器或者客户端会监听,如果有终端通过 名字 A 发送,就会获取到发送的数据,并且把value存到data中,所以说上边的那个data的值就是emitvalue

    emiton一对一。

    运行方法

    ​ 根目录下 右键-终端运行 输入 node index.js

    或 编译器左边项目目录右键打开于终端。在编译器下方的窗口输入指令。

    REPL 命令

    • ctrl + c – 退出当前终端。
    • ctrl + c 按下两次 – 退出 Node REPL。
    • ctrl + d – 退出 Node REPL.
    • 向上/向下 键 – 查看输入的历史命令
    • tab 键 – 列出当前命令

    学习过程

    1. 服务器端的编写

    socket.io 快速入门教程——聊天应用

    Node.js Express 框架

    创建服务器

    使用 http.createServer() 方法创建服务器,并使用 listen 方法绑定端口3000 。 函数通过 request, response 参数来接收和响应数据。

    在项目根目录 新建一个 index.js 文件来创建应用。

    var app = require('express')();
    var http = require('http').Server(app);
    
    app.get('/', function(req, res){
        console.log("主页 POST 请求");
        res.send('<h1>Hello world</h1>');
    });
    
    
    var server = app.listen(3000, function () {
    
        var host = server.address().address
        var port = server.address().port
    
        console.log("访问地址为 http://%s:%s", host, port)
    
    })
    

    这段代码作用如下:

    1. Express 初始化 app 作为 HTTP 服务器的回调函数 (见第 2 行)。
    2. 定义了一个路由/来处理首页访问。在终端输出"主页 POST 请求",给客户端返回'<h1>Hello world</h1>'
    3. 使 http 服务器监听端口 3000。也就是说这个代码只对端口3000有效。

    路由/其实是域名后面的东西。如果访问 localhost:3000/,因为 域名后面只有“/”,所以匹配了这个路由。

    运行node index.js

    2. Node.js

    模块

    引入模块

    Node.js 中,引入一个模块非常简单。

    创建 hello.js 文件,代码如下:

    //hello.js
    exports.world = function() {
      console.log('Hello World');
    }
    //world模块就暴露了,调用见下
    

    创建一个 main.js 文件并引入 hello 模块,代码如下:

    //main.js
    var hello = require('./hello');//引入
    hello.world();//调用
    

    以上实例中,代码 require('./hello') 引入了当前目录下的 hello.js 文件(./ 为当前目录,node.js 默认后缀为 js)。

    Node.js 提供了 exportsrequire 两个对象。

    exports 是模块公开的接口。

    require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。

    有时候我们只是想把一个对象封装到模块中,格式如下:

    module.exports = function() {
      // ...
    }
    

    例如:

    //hello.js 
    function Hello() { 
        var name; 
        this.setName = function(thyName) { 
            name = thyName; 
        }; 
        this.sayHello = function() { 
            console.log('Hello ' + name); 
        }; 
    }; 
    module.exports = Hello;        //暴露接口
    

    这样就可以直接获得这个对象了:

    //main.js 
    var Hello = require('./hello'); 
    hello = new Hello(); 
    hello.setName('BYVoid'); 
    hello.sayHello(); 
    

    Node.js 中自带了一个叫做 http 的模块,我们在我们的代码中请求它并把返回值赋给一个本地变量。

    这把我们的本地变量变成了一个拥有所有 http 模块所提供的公共方法的对象。


    模块的加载

    在路径 Y 下执行 require(X) 语句执行顺序:

    1. 如果 X 是内置模块
       a. 返回内置模块
       b. 停止执行
    2. 如果 X 以 '/' 开头
       a. 设置 Y 为文件根路径
    3. 如果 X 以 './' 或 '/' or '../' 开头
       a. LOAD_AS_FILE(Y + X)
       b. LOAD_AS_DIRECTORY(Y + X)
    4. LOAD_NODE_MODULES(X, dirname(Y))
    5. 抛出异常 "not found"
    
    LOAD_AS_FILE(X)                    //作为文件加载
    1. 如果 X 是一个文件, 将 X 作为 JavaScript 文本载入并停止执行。
    2. 如果 X.js 是一个文件, 将 X.js 作为 JavaScript 文本载入并停止执行。
    3. 如果 X.json 是一个文件, 解析 X.json 为 JavaScript 对象并停止执行。
    4. 如果 X.node 是一个文件, 将 X.node 作为二进制插件载入并停止执行。
    
    LOAD_INDEX(X)                    //加载索引
    1. 如果 X/index.js 是一个文件,  将 X/index.js 作为 JavaScript 文本载入并停止执行。
    2. 如果 X/index.json 是一个文件, 解析 X/index.json 为 JavaScript 对象并停止执行。
    3. 如果 X/index.node 是一个文件,  将 X/index.node 作为二进制插件载入并停止执行。
    
    LOAD_AS_DIRECTORY(X)             //作为目录加载
    1. 如果 X/package.json 是一个文件,
       a. 解析 X/package.json, 并查找 "main" 字段。
       b. let M = X + (json main 字段)
       c. LOAD_AS_FILE(M)
       d. LOAD_INDEX(M)
    2. LOAD_INDEX(X)
    
    LOAD_NODE_MODULES(X, START)        //加载节点模块
    1. let DIRS=NODE_MODULES_PATHS(START)
    2. for each DIR in DIRS:
       a. LOAD_AS_FILE(DIR/X)
       b. LOAD_AS_DIRECTORY(DIR/X)
    
    NODE_MODULES_PATHS(START)        //节点模块路径
    1. let PARTS = path split(START)
    2. let I = count of PARTS - 1
    3. let DIRS = []
    4. while I >= 0,
       a. if PARTS[I] = "node_modules" CONTINUE
       b. DIR = path join(PARTS[0 .. I] + "node_modules")
       c. DIRS = DIRS + DIR
       d. let I = I - 1
    5. return DIRS
    


    回调函数

    回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。


    函数

    一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。

    举例来说,你可以这样做:

    function say(word) {
      console.log(word);
    }
    
    function execute(someFunction, value) {
      someFunction(value);
    }
    
    execute(say, "Hello");
    

    匿名函数

    我们可以把一个函数作为变量传递。但是我们不一定要绕这个”先定义,再传递”的圈子,我们可以直接在另一个函数的括号中定义和传递这个函数:

    function execute(someFunction, value) {
      someFunction(value);
    }
    execute(function(word){ console.log(word) }, "Hello");
    

    我们在 execute 接受第一个参数的地方直接定义了我们准备传递给 execute 的函数。

    用这种方式,我们甚至不用给这个函数起名字,这也是为什么它被叫做匿名函数 。

    全局对象

    __filename

    __filename 表示当前正在执行的脚本的文件名。它将输出文件所在位置的绝对路径,且和命令行参数所指定的文件名不一定相同。 如果在模块中,返回的值是模块文件的路径。

    __dirname

    __dirname 表示当前执行脚本所在的目录。


    文件系统

    Node 导入文件系统模块(fs)语法如下所示:

    var fs = require("fs")
    

    Node.js 文件系统

    Node.js Web 模块

    使用 Node 创建 Web 服务器
    Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:

    var http = require('http');
    

    Node.js Express 框架

    Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

    使用 Express 可以快速地搭建一个完整功能的网站。

    Express 框架核心特性:

    • 可以设置中间件来响应 HTTP 请求。
    • 定义了路由表用于执行不同的 HTTP 请求动作。
    • 可以通过向模板传递参数来动态渲染 HTML 页面。
    请求和响应

    Express 应用使用回调函数的参数: requestresponse 对象来处理请求和响应的数据。

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

    requestresponse 对象的具体介绍:

    Request 对象 – request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。

    常见属性有:

    1. req.app:当callback为外部文件时,用req.app访问express的实例
    2. req.baseUrl:获取路由当前安装的URL路径
    3. req.body / req.cookies:获得「请求主体」/ Cookies
    4. req.fresh / req.stale:判断请求是否还「新鲜」
    5. req.hostname / req.ip:获取主机名和IP地址
    6. req.originalUrl:获取原始请求URL
    7. req.params:获取路由的parameters
    8. req.path:获取请求路径
    9. req.protocol:获取协议类型
    10. req.query:获取URL的查询参数串
    11. req.route:获取当前匹配的路由
    12. req.subdomains:获取子域名
    13. req.accepts():检查可接受的请求的文档类型
    14. req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码
    15. req.get():获取指定的HTTP请求头
    16. req.is():判断请求头Content-Type的MIME类型

    Response 对象 – response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。

    常见属性有:

    1. res.app:同req.app一样
    2. res.append():追加指定HTTP头
    3. res.set()在res.append()后将重置之前设置的头
    4. res.cookie(name,value [,option]):设置Cookie
    5. opition: domain / expires / httpOnly / maxAge / path / secure / signed
    6. res.clearCookie():清除Cookie
    7. res.download():传送指定路径的文件
    8. res.get():返回指定的HTTP头
    9. res.json():传送JSON响应
    10. res.jsonp():传送JSONP响应
    11. res.location():只设置响应的Location HTTP头,不设置状态码或者close response
    12. res.redirect():设置响应的Location HTTP头,并且设置状态码302
    13. res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。
    14. res.send():传送HTTP响应
    15. res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
    16. res.set():设置HTTP头,传入object可以一次设置多个头
    17. res.status():设置HTTP状态码
    18. res.type():设置Content-Type的MIME类型

    app.use
    app.use(path,callback)

    app.use(path,callback)中的callback既可以是router对象又可以是函数。

    app.get(path,callback)中的callback只能是函数。

    当一个路由有好多个子路由时用app.use(path,router)

    例子:

    http://localhost:3000/home/one

    http://localhost:3000/home/two

    http://localhost:3000/home/three

    路由/home后面有三个子路由紧紧跟随,分别是/one,/two,/three,如果使用app.get(),则要不断的重复,很麻烦,也不利用区分。

    我们可以创建一个router.js专门用来一个路由匹配多个子路由。

    var express = require('express')
    var router = express.Router()
    router.get("/",(req,res)=>{
        res.send("/")
    })
    router.get("/one",(req,res)=>{
        res.send("one")
    })
    router.get("/two",(req,res)=>{
        res.send("second")
    })
    router.get("/three",(req,res)=>{
        res.send("three")
    })
    module.exports = router;
    //module.exports把router暴露出去了。
    

    app.js中导入router.js

    var express = require('express')
    var router = require("./router")
    var app = express()
    
    app.use('/home',router) //router路由对象中的路由都会匹配到"/home"路由后面
    app.get('/about', function (req, res) {
      console.log(req.query)
      res.send('你好,我是 Express!')
    })
    app.listen(3000, function () {
      console.log('app is running at port 3000.')
    })
    

    app.use(express.static(‘public’));

    为了提供对静态资源文件(图片,css,js文件)的服务,请使用Express内置的中间函数express.static

    传递一个包含静态资源的目录给express.static中间件用于立即开始提供文件。 比如用以下代码来提供public目录下的图片、css文件和js文件:

    app.use(express.static('public'));
    

    如果前台想请求后台public目录下images/08.jpg静态的图片资源
    通过: http://localhost:3000/images/08.jpg

    通过多次使用express.static中间件来添加多个静态资源目录:

    app.use(express.static('public'));
    app.use(express.static('file'));
    

    Express将会按照你设置静态资源目录的顺序来查看静态资源文件。

    为了给静态资源文件创建一个虚拟的文件前缀(文件系统中不存在),可以使用express.static函数指定一个虚拟的静态目录,如下:

    app.use('/static', express.static('public'));
    

    现在你可以使用‘/static’作为前缀来加载public文件夹下的文件了。

    比如: http:// localhost:3000/static/image/kitten.jpg


    get/post

    get

    “读取“一个资源。比如Get到一个html文件。反复读取不应该对访问的数据有副作用。

    post

    例如,在页面里<form> 标签会定义一个表单。点击其中的submit元素会发出一个POST请求让服务器做一件事。这件事往往是有副作用的,不幂等的。

    post是一种提交。

    4. 客户端的编写

    socket.io 快速入门教程——聊天应用

    在上面的这个代码里面,我们对访问 localhost:3000 采用 res.send() 方法,让其返回一个网页。这样显然对于前端并不友好。

    替代的方法是新建一个 index.html 文件作为服务器响应。

    现在我们用 sendFile 来重构之前的回调:

    app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
    });
    

    __dirname指的是当前文件所在文件夹的绝对路径。

    index.html 内容如下:

    <!doctype html>
    <html>
      <head>
        <title>Socket.IO chat</title>  <!--网页名称-->
        <style>
          * { margin: 0; padding: 0; box-sizing: border-box; }
          body { font: 13px Helvetica, Arial; }
          form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
          form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
          form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
          #messages { list-style-type: none; margin: 0; padding: 0; }
          #messages li { padding: 5px 10px; }
          #messages li:nth-child(odd) { background: #eee; }
        </style>
      </head>
      <body>
        <ul id="messages"></ul>
        <form action="">
          <input id="m" autocomplete="off" /><button>Send</button>
        </form>
      </body>
    </html>
    

    1. margin 属性为“外边距”,指的是围绕在元素边框的空白区域;
    2. Padding 属性为“内边距”,定义元素边框与元素内容之间的空间。
    3. box-sizing 是设置盒子模型用的,他其实是设置容器最终的尺寸的计算方法,盒子的计算方法如下: width(宽度) + padding(内边距) + border(边框) = 元素实际宽度
      height(高度) + padding(内边距) + border(边框) = 元素实际高度 上面的计算方法是按照box-sizing:content-box来说的,根据这种设置办法是,先规定了显示区域的大小,在加上边框和内边距就是元素最终显示的大小 还有另外一种模式,就是box-sizing:border-box,他的使用方式就是,先规定了最终显示的区域大小,实际内容展示区域是根据最终区域大小减去边框和内边距,自适应比较强,有时候比较方便页面布局一些。
    4. font 规定文本的字体、字体尺寸、字体颜色。

    日后再补。

    5. 集成 Socket.IO

    Socket.IO 由两部分组成:

    • 一个服务端用于集成 (或挂载) 到 Node.JS HTTP 服务器: socket.io
    • 一个加载到浏览器中的客户端: socket.io-client

    开发环境下, socket.io 会自动提供客户端。

    Socket.IO 的核心理念就是允许发送、接收任意事件和任意数据。任意能被编码为 JSON 的对象都可以用于传输。

    6. jQuery

    $就是jQuery的别称。

    jQuery就是jQuery库提供的一个函数.(好像也不仅仅只是函数, 因为还有 $.ajax(options) 这样的使用,等同 jQuery.ajax(options))

    这个函数的作用是根据 () 里的参数进行查找和选择html文档中的元素, 函数作用之一就是GetElementByID的代替,但()内不仅可以是ID,还可以是各类选择器。

    比如:

    $(document)就是 选取整个文档对象。

    7. HTML

    ![img](Web 课程设计.assets/02A7DD95-22B4-4FB9-B994-DDB5393F7F03.jpg)

    • <!DOCTYPE html> 声明为 HTML5 文档
    • <html> 元素是 HTML 页面的根元素
    • <head> 元素包含了文档的元(meta)数据,如 <meta charset=“utf-8”> 定义网页编码格式为 utf-8
    • <title> 元素描述了文档的标题
    • <body> 元素包含了可见的页面内容
    • <h1> 元素定义一个大标题
    • <p> 元素定义一个段落

    学习过程中的代码

    前期的学习到这里结束。

    接下来放一段到此为止的代码。

    服务端:

    const { data } = require('jquery');
    const app = require('express')();
    const http = require('http').Server(app);
    const io = require('socket.io')(http);
    
    app.get('/', function(req, res){
        res.sendFile(__dirname + '/index.html');
        console.log("主页 get 请求");
    });
    
    
    io.on('connection', function(socket){
        console.log("一位用户连接");
        socket.on('chat message', function(msg){
            console.log('message: ' + msg);
            io.emit('chat message', msg);
        });
        socket.on('disconnect', function(){
            console.log('用户关闭连接');
        });
    });
    
    http.listen(3000, function(){
        console.log('http://127.0.0.1:3000');
    });
    

    客户端

    <!doctype html>
    <html>
    <head>
        <title>Socket.IO chat</title>
        <style>
            * { margin: 0; padding: 0; box-sizing: border-box; }
            body { font: 13px Helvetica, Arial; }
            form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
            form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
            form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
            #messages { list-style-type: none; margin: 0; padding: 0; }
            #messages li { padding: 5px 10px; }
            #messages li:nth-child(odd) { background: #eee; }
        </style>
    </head>
    <body>
    <ul id="messages"></ul>
    <form action="">
        <input id="m" autocomplete="off" /><button>Send</button>
    </form>
    
    
    </body>
    <script src="/socket.io/socket.io.js"></script>
    <script src="https://code.jquery.com/jquery-1.11.1.js" rel="external nofollow" ></script>
    <script>
        $(function () {
            var socket = io();
            $('form').submit(function(){
                socket.emit('chat message', $('#m').val());
                $('#m').val('');
                return false;
            });
            socket.on('chat message', function(msg){
                $('#messages').append($('<li>').text(msg));
            });
        });
    </script>
    </html>
    

    文件结构

    接下来正式开始。我将在上面代码的基础上修改。


    开始

    本地数据库

    建立数据库

    数据库建表

    index.js文件添加依赖及定义,现在定义部分如下

    const { data } = require('jquery');
    const express=require('express');
    const app = require('express')();
    const http = require('http').Server(app);
    const io = require('socket.io')(http);
    const mysql = require('mysql');
    const url = require('url');
    const fs = require('fs');
    const { Console } = require('console');
    const { query } = require('express');
    const PORT = 3000;
    

    后端代码

    const { data } = require('jquery');
    const express=require('express');
    const app = require('express')();
    const http = require('http').Server(app);
    const io = require('socket.io')(http);
    const mysql = require('mysql');
    const url = require('url');
    const fs = require('fs');
    const { Console } = require('console');
    const { query } = require('express');
    const Front=__dirname +"/Front/";
    const PORT = 3000;
    
    const _cilents=[];          //在线的名称
    
    //数据库连接
    const connection = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: 'mhqdcqxs',
        database: 'ChatProject'
    });
    
    //从数据库中查询
    function select_user(data,callback){
        //连接数据库开始查询
        let sql = 'SELECT * FROM user where ac = \''+data.username+'\';';
        connection.query(sql,function (err, result) {
            if(err){
                console.log('[SELECT ERROR] - ',err.message);
                callback(null)
                //如果查不到就返回null
            }
            //用回调函数表示执行完了。回调函数是哪个?取决于传参。
            //查到了就返回结果
            callback(result);
        });
    }
    
    //插入数据库
    function insert_user(data){
        let sql ='INSERT INTO user VALUES (\''+data.username+'\',\''+data.password+'\');';
        connection.query(sql,(err,result)=>{
            if(err){
                console.log('[INSERT ERROR] - ',err.message);
                return;
            };
        });
    }
    
    app.get('/', function(req, res){
        res.sendFile(Front + 'index.html');
        console.log("主页 get 请求");
    });
    
    io.on('connection', function(socket){
        socket.on('login',function(data){
    
            //寻找是否在线
            var t_cilent = _cilents.find(item => item == data.username);
            if(t_cilent){
                socket.emit('loginFail','该用户已经登录');
                return ;
            }
    
            //从数据库中寻找
            select_user(data,result=>{
                if(result.length){
                    if(result[0].pw != data.password){
                        socket.emit('loginFail','密码错误!');
                        return ;
                    }
                }
                else{
                    insert_user(data);
                }
    
                //记录已经登录的用户
                _cilents.push(data.username);
    
                socket.emit('loginSuccess',_cilents);
    
                //全局广播,来了新用户
                io.emit('updateUser', _cilents);
                socket.username = data.username;
            })
        });
    
        // 用户断开连接的功能
        socket.on('disconnect', () => {
            // 把当前用户的信息删除
            // 找到断开连接的用户的下标
            const idx =_cilents.findIndex(item => item == socket.username)
            // 删除函数,第一个参数是下标,第二个是距离,也就是删几个元素
            _cilents.splice(idx, 1);
            // 告诉所有人,用户发生更新
            io.emit('updateUser', _cilents)
        });
    
        socket.on('sendMessage',data=>{
            var toSocket = null
            //找到接收者在io.sockets.sockets中的位置。
            for (const key in io.sockets.sockets) {
                if (io.sockets.sockets[key].username == data.toName) {
                    toSocket = key
                    break
                }
            }
            if (toSocket) {
                // 发送给指定用户
                socket.to(toSocket).emit('receiveMessage', {
                    msg:data.msg,
                    fromName:data.name,
                    time : data.time,
                    name:data.toName
                })
            }
    
            //将数据存入文件中
            let url = './history/'+data.name+'To'+data.toName+'.txt';
            fs.appendFile(url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
            if(data.toName != data.name){
                let t_url = './history/'+data.toName+'To'+data.name+'.txt';
                fs.appendFile(t_url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
            }
        });
    
        socket.on('sendImage',data=>{
            var toSocket = null
            //console.log(io.sockets.sockets)
            for (const key in io.sockets.sockets) {
                if (io.sockets.sockets[key].username == data.toName) {
                    toSocket = key
                    break
                }
            }
            //console.log(data);
            if (toSocket) {
                // 发送给指定用户
                socket.to(toSocket).emit('receiveImage', {
                    image:data.image,
                    fromName:data.name,
                    time : data.time,
                    name:data.toName
                })
            }
    
            //将数据存入文件中
            let url = './history/'+data.name+'To'+data.toName+'.txt';
            fs.appendFile(url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
            if(data.toName != data.name){
                let t_url = './history/'+data.toName+'To'+data.name+'.txt';
                fs.appendFile(t_url,'\n'+JSON.stringify(data), err => {if (err) {console.log(err)}});
            }
        })
    
    });
    
    http.listen(PORT, function(){
        console.log('http://127.0.0.1:'+PORT+'/');
    });
    

    后面没有了,开发时间太紧没有继续记录,但以上作为一个前置步骤已经比较完整。