NodeJS原理

本文深入探讨NodeJS模块化编程,包括自定义模块、第三方模块的使用,以及如何搭建静态资源服务器。同时,详细讲解WebSocket协议,演示如何使用ws模块创建WebSocket服务器,实现客户端与服务端的实时通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NodeJS

http服务器

  • 静态资源服务器

    • 使用模块
      • http
      • fs
      • url
      • path
    • 了解文件mime类型
        const http = require('http');
        const fs = require('fs');
        const url = require('url')
        const path = require('path');
        const mine = require('./module/mime')
    
        const server = http.createServer((req,res)=>{
            const {pathname} = url.parse(req.url);
            const realpath = path.join(__dirname,pathname);
            const extname = path.extname(pathname).substring(1);
            fs.readFile(realpath,(err,data)=>{
                if(err){
                    return res.end('404');
                }
                res.writeHead(200,{'content-type':mime[extname] + ';charset=utf8'});
                res.end(data);
            })
        })
    
        server.listen(1000,()=>{
            console.log('server is running on port 1000')
        })
    

mime.js 文件

module.exports = {
  "css": "text/css",
  "gif": "image/gif",
  "html": "text/html",
  "ico": "image/x-icon",
  "jpeg": "image/jpeg",
  "jpg": "image/jpeg",
  "js": "text/javascript",
  "json": "application/json",
  "pdf": "application/pdf",
  "png": "image/png",
  "svg": "image/svg+xml",
  "swf": "application/x-shockwave-flash",
  "tiff": "image/tiff",
  "txt": "text/plain",
  "wav": "audio/x-wav",
  "wma": "audio/x-ms-wma",
  "wmv": "video/x-ms-wmv",
  "xml": "text/xml"
};
  • 部署到服务器

模块化规范

  • CommonJS-------node.js
  • CMD-------------- require.js
  • AMD---------------sea.js
  • ES Module---------ES6

模块分类

Nodejs 模块系统采用commonJS规范。一般情况模块可分为三类:

  • 原生模块(Nodejs内置模块)

  • 自定义模块

    • 文件
    • 目录
  • 第三方模块

    通过npm install命令安装到node_modules目录

    //hello.js

    function hello(){
        return 'hello laoxie';
    }

    //对外暴露接口(commonJS规范)
    module.exports = hello;

原理

在nodejs中,任何一脚本文件都会自动写入一个包装函数中(所以每个模块具有独立作用域),并传入以下参数

  • exports: 当前模块的导出对象
  • require: 引入模块的方法
  • module: 当前模块对象
  • __filename: 当前文件的绝对路径
  • __dirname: 当前文件所在目录的绝对路径
    (function(exports,require,module,__filename,__dirname){
        // 我们编写的模块代码
    })

导出模块

如果没有这句话,引入模块时 就会得到 空对象

  • module.exports

对外暴露单个接口,一个模块中只能有一个 module.exports,多个会被覆盖。

  • exports

exports 为 module.exports的引用,一个模块中可以使用多次

    //person.js
    function setName(){
        return 'fqniu'
    }

    function setAge(){
        return 25
    }

    // 对外暴露接口
    exports.setName = setName;
    exports.setAge = setAge;

引入模块: require()

引入模块,使用nodejs内置的require()方法,一种同步的模板引入方式

    //page.js
    
    //得到一个对象,包含暴露的setName,setAge方法
    let person = require('./person.js');

    // 既然是得到对象,也可以直接解构
    let {setName,setAge} = require('./person.js');

  • require 方法中的文件查找策略,如下:
    在这里插入图片描述

常用第三方的模块

request模块

可用于发起 http 或 https 请求,可理解成服务端的 ajax 请求。可用于简单的服务器代理,用法和 ajax 类似。

  • 安装: npm install request --save

  • GET 请求

    const request = require('request');

    request.get('https://cnodejs.org/api/v1/topics?page=1&limit=10', (error, response, body) => {
        // error: 错误信息,默认null
        // response: 相应对象
        // body: 请求响应内容
        console.log(body)
    });
    //or
    request('https://cnodejs.org/api/v1/topics?page=1&limit=10', (error, response, body) => {
        console.log(body)
    });
  • 参数设置
    const request = require('request');
    request({
        url: url,
        method: 'get',
        timeout: 10000,
        headers: {},
        proxy: {} ,
        agentOptions: {},
        params: {}
    }, function(err, res, body) {
    
    });
  • Stream流
    request('http://img.zcool.cn/community/018d4e554967920000019ae9df1533.jpg@900w_1l_2o_100sh.jpg').pipe(fs.createWriteStream('test.png'))
    request('https://cnodejs.org/api/v1/topics?page=1&limit=10').pipe(fs.createWriteStream('cnodejs.json'));

cheerio模块

一个专门为服务器设计包含jquery核心库的第三方模块

  • cheerio.load(html)

用于在于html代码以便进一步的精确选取,它不会执行html代码中的css和js

  • 爬虫应用(爬取图片到本地)

又被称为网页蜘蛛,网络机器人,主要是在服务端去请求外部的 url 拿到对方的资源,然后进行分析并抓取有效数据。

    //这里用 request 实现一个简单的图片抓取的小爬虫
    const request = require('request');
    const fs = require('fs');
    const cheerio = require('cheerio');// cheerio为包含jQuery 核心的子集
    const path = require('path');

    request('http://www.lanrentuku.com/', (error, response, body) => {
        // 执行load方法载入获取到的html结构
        let $ = cheerio.load(body);

        // 利用jquery的核心方法获取html代码中的具体元素
        $('img', '.in-ne').each((i, e) => {
            let src = $(e).attr('src');
            let filename = path.basename(src);

            // 利用request与stream数据流保存爬取到的图片到本地硬盘
            request(src).pipe(fs.createWriteStream(filename))
        })
    });
  • stream流

    Stream 是一个抽象接口,往往用于打开大型的文本文件,创建一个读取操作的数据流。Node 中有很多对象实现了这个接口。如http 服务器发起请求的request 对象就是一个 Stream。所有的 Stream 对象都是 EventEmitter 的实例

    • 所谓大型文本文件,指的是文本文件的体积很大,读取操作的缓存装不下,只能分成几次发送,每次发送会触发一个data事件,发送结束会触发end事件。

    • 读取流(以流的方式读取文件内容)

      • setEncoding():设置读取编码格式
      • data事件:读取数据中
      • end事件:数据读取完毕
        var fs = require("fs");
        var data = '';
    
        // 创建可读流
        // 以流的方式读取input.txt中的内容
        var readerStream = fs.createReadStream('input.txt');
        // console.log(readerStream);
    
        // 设置编码为 utf8。
        readerStream.setEncoding('UTF8');
    
        // 处理流事件 --> data, end, and error
        readerStream.on('data', function(chunk) {
        data += chunk;
        });
    
        readerStream.on('end',function(){
        console.log(data);
        });
    
        readerStream.on('error', function(err){
        console.log(err.stack);
        });
    
    • 写入流(以流的方式写入文件)
      • write()方法:写入内容方法
      • end()方法:标记写入结束
      • finish事件:写入完成后执行
        //创建一个可以写入的流,写入到文件 output.txt 中
    
        var fs = require("fs");
        var data = '中国';
    
        // 创建一个可以写入的流,写入到文件 output.txt 中
        // var writerStream = fs.createWriteStream('output.txt', {'flags': 'a'}); //追加文本
        var writerStream = fs.createWriteStream('output.txt');
    
        // 使用 utf8 编码写入数据
        writerStream.write(data,'UTF8');
    
        // 标记文件末尾
        writerStream.end();
    
        // 处理流事件 --> data, end, and error
        writerStream.on('finish', function() {
            console.log("写入完成。");
        });
    
        writerStream.on('error', function(err){
           console.log(err.stack);
        });
    
    • 管道流pipe

    管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。我们把文件比作装水的桶,而水就是文件里的内容,我们用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就慢慢的实现了大文件的复制过程。

        var fs = require("fs");
    
        // 创建一个可读流
        var readerStream = fs.createReadStream('input.txt');
    
        // 创建一个可写流 
        var writerStream = fs.createWriteStream('output.txt');
    
        // 管道读写操作
        // 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
        readerStream.pipe(writerStream);
    
    • 链式流(多个pipe)

    链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。接下来我们就是用管道和链式来压缩和解压文件。

WebSocket

ws模块

协议

HTTP/HTTPS 协议

http/https协议可以总结几个特点:

  • 一次性的、无状态的短连接:客户端发起请求、服务端响应、结束。
  • 被动性响应:只有当客户端请求时才被执行,给予响应,不能主动向客户端发起响应。
  • 信息安全性:得在服务器添加 SSL 证书,访问时用 HTTPS。
  • 跨域:服务器默认不支持跨域,可在服务端设置支持跨域的代码或对应的配置。
TCP 协议

TCP 协议可以总结几个特点:

  • 有状态的长连接:客户端发起连接请求,服务端响应并建立连接,连接会一直保持直到一方主动断开。
  • 主动性:建立起与客户端的连接后,服务端可主动向客户端发起调用。
  • 信息安全性:同样可以使用 SSL 证书进行信息加密,访问时用 WSS 。
  • 跨域:默认支持跨域。
WebSocket 协议

WebSocket 目前由 W3C 进行标准化。WebSocket 已经受到 Firefox 4、Chrome 4、Opera 10.70 以及Safari 5 等浏览器的支持。 如果在前端我们可以把 AJAX 请求当作一个 HTTP 协议的实现,那么,WebSocket 就是 TCP 协议的一种实现。

使用Socket

服务端
  • 安装ws模块
    npm install ws
  • 开启WebSocket服务器
    let socketServer = require('ws').Server;
    let wss = new socketServer({
        port: 1001
    });
  • 配合express开启服务器
    let express = require('express');
    let http = require('http');
    let ws = require('ws');

    let app = express();
    let server = http.Server(app);
    let SocketServer = ws.Server;
    let wss = new SocketServer({
        server,
        port:1001
    });
  • 用 on 来进行事件监听
    • connection:连接监听,当客户端连接到服务端时触发该事件,返回连接客户端对象
      • close:连接断开监听,当客户端断开与服务器的连接时触发
      • message:消息接受监听,当客户端向服务端发送信息时触发该事件
      • send: 向客户端推送信息
客户端

WebSocket是HTML5开始提供的一种基于 TCP 的协议,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据

  • 实例化 WebSocket ,参数为 WebSocket 服务器地址,建立与服务器的连接
  • 事件
    • onopen:当网络连接建立时触发该事件
    • onclose:当服务端关闭时触发该事件
    • onerror:当网络发生错误时触发该事件
    • onmessage:当接收到服务器发来的消息的时触发的事件,也是通信中最重要的一个监听事件
  • 方法
    • close(): 在客户端断开与服务端的连接 socket.close();
    • send():向服务端推送消息
    //连接 socket 服务器
    var socket = new WebSocket('ws://localhost:1001');
   
    //监听 socket 的连接
    socket.onopen = function(){
       document.write("服务已连接 ws://localhost:1001");
    }
   
    //监听服务端断开
    socket.onclose = function(){
        document.write("服务已断开");
        socket = null;
    }

    //监听服务端异常
    socket.onerror = function(){
        document.write("服务错误");
        socket = null;
    }

    //监听服务端广播过来的消息
    socket.onmessage = function(msg){
        var msgObj = JSON.parse(msg.data);
        if(msgObj.status == 0){
            $('<p>' + msgObj.nickname + '[' + msgObj.time + ']退出聊天</p>').appendTo('.msgList');
        } else{
            $('<p>' + msgObj.nickname + '[' + msgObj.time + ']:' + msgObj.message + '</p>').appendTo('.msgList');
        }
    }

    var sendMessage = function(_mess){
        if(socket){
            var myDate = new Date();
            var now = myDate.getMonth() + '-' + myDate.getDate() + ' ' + myDate.getHours() + ':' + myDate.getMinutes() + ':' + myDate.getSeconds();             
            
            var mesObj = {
                nickname: $('#nickName').val(),
                message: _mess || $('#mesBox').val(),
                time: now
            }
            //向服务端发送消息
            socket.send(JSON.stringify(mesObj));
        }           
    }

常用第三方模块工具

  • cnpm 包管理工具
  • yarn 包管理工具
  • gulp 构建工具
  • supervisor 自动重启工具
  • http-server 静态资源服务器
  • json-server mock数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值