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

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

参考文章:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容

  • 电梯上已经没有人了,但它还在播报着,请握紧扶手,注意脚下,不要倚靠扶梯。有人来了,地铁口又热闹了。有教训娃娃不要乱...
    没有心思阅读 152评论 0 0
  • 图解计算机 由上图可知:计算机系统由硬件系统和软件系统组成 1.计算机硬件系统从逻辑上主要由运算器、控制器、存储器...
    莫恋初阅读 843评论 0 1
  • 林深时见鹿,海蓝时见鲸,梦醒时见你 可实际:林深时雾起,海蓝时浪涌,梦醒时夜续 未见鹿,未见鲸,亦未见你。 但:鹿...
    小丑豹阅读 160评论 0 0
  • 胡璨作文集2017-083【暑假作文57】海底小纵队2祁门县阊江小学302班 胡璨2017年8月26日 星期六 晴...
    胡璨HUCAN阅读 891评论 0 6
  • 姓名 :赵乐坤 图片发自简书App 企业名称:阿里米丁节能科技(苏州)有限公司 组别 谦虚二组 【日精进打卡第71...