美文网首页
node学习二 (url、http、事件循环和回调函数)

node学习二 (url、http、事件循环和回调函数)

作者: 淡退 | 来源:发表于2020-01-04 19:57 被阅读0次

    http模块

    1. 创建服务器两种方式
    // 方式一 http.createServer([requestListener])
    /*
     * [requestListener(req, res)] 用户请求后回调函数,有两个参数,req请求;res响应
     * @return http.Server 返回一个http.Server实例
     */
    var server = http.createServer((req, res) => {
        res.writeHeader(200,{'Content-Type':'text/plain'});
        res.end('hello world');
    })
     // 方式二 new http.Server()
     var server = new http.Server();
     server.on('request', (req, res){
        res.writeHeader(200,{'Content-Type':'text/plain'});
            res.end('hello world');
     })
    

    采用上面的方式并不能创建一个完成的服务器,还必须监听对应的端口,比如采用下面这种方式。

    // 表示引入http模块
    const http = require('http');
    
    // 创建一个http服务
    /*
       request: 获取url传过来的信息  请求头
       response: 像浏览器响应信息  响应头
    */
    http.createServer(function(request,response){
        // 设置响应头
        response.writeHead(200,{'Content-Type': 'text/plain'});
    
        response.write('1111');// 像页面返回的内容
        response.write('222');
        // 像浏览器输出一句话 并结束响应
        response.end('1123122'); // 
    
    }).listen(8081);// 端口
    

    为啥要监听端口(listen(8080))?

    端口的功能
    一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区 分不同的服务的。

    客户端通常对它所使用的端口号并不关心,只需保证该端口号在本机上是唯一的就可以了。客户端口号又称作临时端口号(即存在时间很短暂)。这是因为它通常只是在用户运行该客户程序时才存在,而服务器则只要主机开着的,其服务就运行。

    简单理解:IP就是一个电脑节点的网络物理地址,就像你的家住的那个地址;端口是该计算机逻辑通讯接口,不同的应用程序用不同的端口,就像你家里的各个不同的房间,卧室用来睡觉,餐厅用来吃饭。
    理解为 node启动一个服务就是将本机当做了服务器,所以它的ip地址是127.0.0.1 ,其中端口8080端口里面是提供你要的服务。

    Url模块

    在上面创建的那个服务器里面,页面所有的请求都会执行createServer中传入的方法。但是不同的请求,需要做不同的处理,所以我们需求判断请求路径,以便我们做出不同的响应。

    我们可以很简单的获取到请求的url,但有数据提交上来的url是十分复杂和不确定的。很不利于编写业务逻辑。所以我们就要将url拆分成我们能用的数据。node的url模块就是帮助我们对提交上来的url进行解析处理

    常用方法:

    parse(urlStr,queryString,AnalysisHost)
    

    解析url,返回一个url属性对象
    urlStr: 要解析的url地址
    queryString: 解析出来的query是字符串还是查询对象,true是对象 false是字符串
    AnalysisHost: 是否要解析出来主机名

    实例代码

      var url = require('url')
      var obj = url.parse('http://www.baidu.com/vdsa?ie=utf-8&word=sad',true,true)
      console.log(obj);
    

    结果:


    Url组成部分:

    • protocol:url的通信协议(http/https)
    • slashes:如果协议protocol冒号后跟的是两个斜杠字符(/),那么值为true
    • auth:URL的用户名与密码部分
    • host:url的主机名 “baidu.com”
    • port: 端口号
    • hostname: hostname是host属性排除端口port之后的小写的主机名部分
    • hash:哈希#后面字符串包括#
    • search:URL的查询字符串部分,包括开头的问号字符(?)
    • query: 不包含问号(?)的search字符串
    • pathname:URL的整个路径部分。跟在host后面,截止问号(?)或者哈希字符(#)分隔
    • path:由pathname与search组成的串接,不包含hash字符后面的东西
    • href:解析后的完整的URL字符串,protocol和host都会被转换成小写。

    事件循环和回调函数

    我们都知道,不同的请求对应的Content-type是不一样的,比如一个页面的加载html和css的Content-type是不一样的。

    文件目录为:


    image.png

    mime.json文件的内容为:

    { ".323":"text/h323" ,
      ".3gp":"video/3gpp" ,
      ".asc":"text/plain" ,
      ".htm":"text/html" ,
      ".html":"text/html" 
    .....
    }
    

    我们现在通过getTypeFile方法来获取不同文件后缀对应的Content-type。

    const http = require('http');
    const fs = require('fs');
    const path = require('path');
    const URL = require('url');
    const utils = require('./utils/getType');
    const EventEmitter = require('./utils/Listener');
    http.createServer(function(request,response){
        const pathname = request.url;
        let extname = path.extname(URL.parse(pathname).pathname); // 获取文件后缀名
     if(pathname !== '/favicon.ico'){
             返回对应的页面
            response.writeHead(200,{
                'Content-Type': `${utils.getTypeFile(extname)};charset='utf-8'`
             });
                fs.readFile('./static/'+URL.parse(pathname).pathname,    (err,data)=> {
                        response.end(data);
                    }
                })
        }else {
            response.end()
        }        
    }).listen(8081)
    console.log('服务启动了')
    

    getTypeFile方法为:

    exports.getTypeFile = function(exname){
        const obj = fs.readFileSync('./mime.json').toString();
        return JSON.parse(obj)[exname];
    }
    

    当getTypeFile是一个同步方法的时候,上面的代码是没有任何问题的。但是如果使用fs.readFile这个异步方法的时候就会出现问题。因为外面得不到返回的值。这里readFildSync是一个同步的方式 ,如果采用异步的方法 readFile 在app.js 如何得到想要值呢 。

    一般解决这种问题的有事件循环、和回调函数的解决方式。

    回调函数

    // app.js
    const utils = require('./utils/getType');
    ...code
    utils.getTypeFild(exname,function(str){
        response.writeHead(200,{
                'Content-Type': `${str};charset='utf-8'`
            });
        ... code    
    })
    
    // getType.js
    exports.getTypeFile = function(exname,callback){
        fs.readFile('./mime.json',(err,data) =>{
            callback(data.toString);
        })
    }
    

    通过在给getTypeFile传入一个回调方法,在readFile执行完成后,在执行回调方法。Node 使用了大量的回调函数,Node 所有 API 都支持回调函数

    事件驱动

    event 模块只提供了一个对象 events.EventEmitter,
    核心就是事件触发和事件监听功能,原生使用原理类似于观察者模式。

    创建 eventEmitter 对象

    // 引入 events 模块
    var events = require('events');
    // 创建 eventEmitter 对象
    var eventEmitter = new events.EventEmitter();
    

    以下程序绑定事件处理程序:

    // 绑定事件及事件的处理程序
    eventEmitter.on('eventName', eventHandler);
    

    我们可以通过程序触发事件:

    // 触发事件
    eventEmitter.emit('eventName');
    

    所以如果采用事件驱动驱动的方式我们的第二种实现方式为:

    1. getType.js
    const fs = require('fs');
    const events= = require('events');
    const EventEmitter = new events.EventEmitter();// 实例化事件对象
    exports.getTypeFile = function(exname){
        fs.readFile('./mime.json',(err,data) =>{
           EventEmitter.emit('exname', data.toString()); // 发送一个名为exname 的广播
        })
    }
    
    2. app.js
    const fs = require('fs');
    const events= = require('events');
    const EventEmitter = new events.EventEmitter();
    ...code
    
    if(pathname !== '/favicon.ico'){
            // 接受一个名为 exname的广播
            EventEmitter.on('exname',(data) =>{
                response.writeHead(200,{
                'Content-Type': `${data};charset='utf-8'`
                });
                ...code;
            })        
    }
    

    这里有一个错误的地方是,两个文件中都通过new events.EventEmitter()来生成了一个EventEmitter对象,尽管他们两个的名字一样,但是他们两个是不同的实例。所以会发现在app.js中并没有接受到一个名为 exname的广播。

    为了保证是一个EventEmitter对象,我们采用这种方法,将app.js、和getType.js中的

    const events= = require('events');
    const EventEmitter = new events.EventEmitter();// 实例化事件对象
    

    替换为

    const EventEmitter = require('./Listener');
    
    // Listenser.js
    const events = require('events');
    const EventEmitter = new events.EventEmitter();
    module.exports = EventEmitter;
    

    这样两个文件使用的就是同一个EventEmitter

    参考文章:

    相关文章

      网友评论

          本文标题:node学习二 (url、http、事件循环和回调函数)

          本文链接:https://www.haomeiwen.com/subject/wnffactx.html