zoukankan      html  css  js  c++  java
  • Node.js 实现的简易服务器 (三)

    index.js

    const server = require('./server');
    const router = require('./router');
    
    server.start(router.route);
    

    server.js

    const http = require('http');  // Node.js 内置的 http 模块
    const url = require('url');  // Node.js 内置的 url 模块
    
    
    function start(route, port = 8080) {
        /**
         * 请求事件发生时调用的回调函数 (Node.js 是执行单线程异步非阻塞事件驱动的)
         */
        function requestListener(req, resp) {
            let pathname = url.parse(req.url).pathname;
            console.log(`Request for ${pathname} received.`);
    
            route(pathname, resp, req);
        }
    
    
        /**
         * 创建一个基础的 HTTP 服务器
         * 
         * 请求 http 模块提供的函数 createServer 创建一个服务器对象, 并调用该对象的 listen 方法监听 8080 端口
         * 
         * See Also: 
         * createServer 函数的源码解析: https://blog.csdn.net/THEANARKH/article/details/88385964
         * Node.js 是事件驱动: https://segmentfault.com/a/1190000014926921
         */
        http.createServer(requestListener).listen(port);
    
        console.log(`Server has started on ${port}`);
    }
    
    
    module.exports = {
        start
    }
    

    router.js

    const requestHandlers = require('./requestHandlers');
    
    // 路由注册
    const handler = {
        '/': requestHandlers.index,
        '/index': requestHandlers.index,
        '/start': requestHandlers.start,
        '/upload': requestHandlers.upload,
        '/show': requestHandlers.show,
    };
    
    // 路由处理
    function route(pathname, resp, req) {
        console.log(`About to route a request for ${pathname}`);
        let handle = handler[pathname];
        if (typeof handle === 'function') {
            handle(resp, req);
            console.log(`${handle.name} was called`);
        } else {
            console.log(`No request handle found for ${pathname}`);
            requestHandlers.NotFound(resp);
        }
    }
    
    
    module.exports = {
        route
    }
    

    requestHandlers.js

    const fs = require('fs');
    const querystring = require('querystring');
    
    // third-party modules
    const formidable = require('formidable');
    
    
    function index(resp) {
        fs.readFile('./index.html', function (err, data) {
            if (err) {
                ServerError(err, resp);
            } else {
                resp.writeHead(200, { 'Content-Type': 'text/html' });
                resp.write(data);
                resp.end();
            }
        });
    }
    
    
    function start(resp) {
        const exec = require('child_process').exec;
        let result = 'empty';
    
        setTimeout(function () {
            exec(`echo 'hello'`, function (err, stdout, stderr) {
                result = stdout;
                console.log(result);
                resp.writeHead(200,);
                resp.write(`You've sent: ${result}`);
                resp.end();  // 等待事件循环调用回调函数拿到结果后才完成响应
            });
        }, 10000);
    }
    
    
    function upload(resp, req) {
        let form = new formidable.IncomingForm();
        form.parse(req, function (err, fields, files) {
            if (err) {
                ServerError(err, resp);
            } else {
                let imgName = `${fields.imgName  || new Date().getTime().toString()}.jpg`;
                try {
                    fs.renameSync(files.upload.path, imgName);
                } catch {
                    imgName = `${new Date().getTime().toString()}.jpg`;
                    fs.renameSync(files.upload.path, imgName);
                }
                resp.writeHead(200, { 'Content-Type': 'text/html' });
                resp.write(`<img src="/show?imgName=${imgName}">`);
                resp.end();
            }
        })
    }
    
    
    function show(resp, req) {
        let imgName = querystring.parse(req.url.split('?')[1]).imgName;
        fs.readFile(imgName, 'binary', function (err, file) {
            if (err) {
                ServerError(err, resp);
            } else {
                resp.writeHead(200, { 'Content-Type': 'image/jpg' });
                resp.write(file, 'binary');
                resp.end();
            }
        })
    }
    
    
    function NotFound(resp, req) {
        resp.writeHead(200, { 'Content-Type': 'text/html' });
        resp.write(`<h1>NOT FOUND</h1>`);
        resp.end();
    }
    
    
    function ServerError(err, resp, req) {
        resp.writeHead(500, { 'Content-Type': 'text/plain' });
        resp.write(err + '
    ');
        resp.end();
    }
    
    
    module.exports = {
        index,
        start,
        upload,
        show,
        NotFound
    }
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Demo</title>
    </head>
    
    <body>
        <form action="/upload" enctype="multipart/form-data" method="post">
            <input type="text" name="imgName">
            <input type="file" name="upload" multiple>
            <input type="submit" value="Upload file">
        </form>
    </body>
    
    </html>
    

    特点

    • 请求处理程序进行阻塞操作时, 会阻塞其他请求的处理
      (原因: 主(执行)线程被阻塞代码阻塞后, 其余所有请求必须等待该阻塞代码处理完毕之后才能执行)
    • 请求处理程序进行非阻塞操作时, 可以正确返回响应内容
      (原因: 请求处理程序是以阻塞方式运行的, 非阻塞代码的回调函数执行时通过 resp 对象返回正确的响应内容)

    总结

    该服务器分为三层:

    • server - 用于启动服务器, 接收请求与返回响应
    • router - 用于路由注册和路由处理
    • requestHandlers - 用于编写业务逻辑, 实现各个请求处理函数

    各层之间的通信顺序为: req -> server -> router -> requestHandlers -> router -> server -> resp, 采用将服务器响应对象传递给请求处理程序, 在请求处理程序中返回响应的方式, 使用了一个三方模块 formidable 更方便的处理表单, 同时新增了一个上传 jpg 图片的功能

    姊妹篇见->Node.js 实现的简易服务器 (二) (非阻塞处理存在问题)

  • 相关阅读:
    cmd常用命令大全
    网卡物理地址
    想看密码的请心平气和听我说
    作为一个程序员仪表
    960,950栅格化方法
    为什么要拿宽960做栅格化呢
    960栅格化优势
    虎牌 查询 自选号
    视频
    在线学习视频地址
  • 原文地址:https://www.cnblogs.com/ayuuuuuu/p/13828448.html
Copyright © 2011-2022 走看看