L3F.WIN

Github及Hexo的使用

0%

Node即学即用

Node即学即用

第1章 Node.js简介

开始写代码

  1. Node REPL(Read - Evaluate - Print - Loop) REPL模式输入,求值,输出,循环
    我们可以启动node,来进行测试一般的Javascript

    1
    2
    3
    4
    5
    6
    7
    8
    node
    >3>2>1
    false
    >true==1
    true
    >true===1
    false
    .exit //输入.exit退出node环境
  2. 编写首个服务器程序

    1
    2
    3
    4
    5
    6
    var http = require('http');
    http.createServer(function (req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
    }).listen(8000, "127.0.0.1");
    console.log('Server running at http://127.0.0.1:8000');

    require(‘http’),导入http模块
    req参数,请求对象
    res参数,响应对象
    res.writeHead设置相应头
    end方法会关闭HTTP连接

非常有用的元命令

一般以 . 开头

命令 功能
.help 显示帮助菜单
.clear 清楚当前运行的内容
.exit 退出Node解析器

第2章 编写有趣的应用

创建聊天室

代码1 创建一个聊天室的基础服务

1
2
3
4
5
6
7
8
9
10
11
12
var net = require('net'); //加载net模块

var chatServer = net.createServer()

chatServer.on('connection', function(client){ //on调用connection事件
client.write("Hi!\n");
client.write("Bye!\n");

client.end()
})

chatServer.listen(3000);

代码2 监听所有链接请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var net = require('net');

var chatServer = net.createServer()

chatServer.on('connection', function(client){
client.write("Hi!\n");

client.on('data', function(data){ //关注data事件
console.log(data.toString()); //不使用toString,后台将显示Buffer后的十六进制字符
})
//此处删除了client.end,为了持续受到数据
})

chatServer.listen(3000);

代码3 添加用户列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var net = require('net');

var chatServer = net.createServer(),
clientList = [];

chatServer.on('connection', function(client){
client.write("Hi!\n");
clientList.push(client);

client.on('data', function(data){
for(var i=0; i<clientList.length; i++){
//把数据发给用户
clientList[i].write(data)
}
console.log(data.toString());
})
})

chatServer.listen(3000);

完成后多用户可以同时聊天,问题是收到自己的重复信息

代码4 完善用户对话

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var net = require('net');

var chatServer = net.createServer(),
clientList = [];

chatServer.on('connection', function(client){
client.name= client.remoteAddress + ':' + client.remotePort; //获取用户地址及port
client.write("Hi " + client.name + '!\n');
clientList.push(client);

client.on('data', function(data){
broadcast(data, client);
console.log(data.toString());
})
})

function broadcast(message, client){
for(var i=0; i<clientList.length; i++){
if(client !== clientList[i]){
clientList[i].write(client.name + ' says ' + message);
}
}
}

chatServer.listen(3000);

虽然已经完善了不少,问题还是很多,退出用户会出严重错误

代码5 完善后的最终版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var net = require('net');

var chatServer = net.createServer(),
clientList = [];

chatServer.on('connection', function(client){
client.name= client.remoteAddress + ':' + client.remotePort;
client.write("Hi " + client.name + '!\n');
console.log(client.name + " joined");
clientList.push(client);

client.on('data', function(data){
broadcast(data, client);
})

client.on('end', function(){
console.log(client.name + " quit");
clientList.splice(clientList.indexOf(client),1) //从用户列表中珊瑚已下线的用户
})

//记录错误
client.on('error', function(){
console.log(e)
})
})

function broadcast(message, client){
var cleanup = [];
for(var i=0; i<clientList.length; i++){
if(client !== clientList[i]){
//检查socket的可写状态
if(clientList[i].writable){
clientList[i].write(client.name + ' says ' + message);
}else{
cleanup.push(clientList[i]);
clientList[i].destroy(); //将用户关闭并移除
}
}
}
for(i=0; i<cleanup.length; i++){
clientList.splice(clientList.indexOf(cleanup[i]), 1);
}
}

chatServer.listen(3000);

增加了对退出用户的删除,错误记录。大家可以慢慢研究

编写Twitter

此处使用Express模块,先创建项目目录,进入目录后输入以下命令

1
npm install --save express

安装完成后我们就可以继续了。首先创建件的Express服务器

创建服务器

1
2
3
4
5
6
7
8
9
#server.js
var express = require('express');
var app = express(); //新版已经不适用createServer()来创建了。统一改用express()

app.get('/', function(req, res){
res.send("Welcome to Node Twitter"); //send()方法包含了HTTP头
});

app.listen(3000);

添加基础API

第3章 编写健壮的Node程序

编写Node.js服务器程序的时候遵循的两个策略

  • 在设置完成以后,所有操作都是事件驱动的
  • 如果Node.js需要长时间处理数据,考虑把它分配给web Worker去处理

模式

  1. 无序的并行I/O
    Node中所有的I/O操作默认都是无序并行,无序并非乱序,是指顺序无法保证。
  2. 顺序串行I/O
    每一个人物都必须在上一个任务完成后才能开始。

编写产品代码

编写成熟的代码,是程序更容易维护,更简洁,更易懂。

差错处理

在Node.js中使用error时间来处理异步错误

req.on('error', function(e){...})

使用多处理器

Node.js 可以使用 cluster模块来把任务分配给子进程

第4章 核心API

Events

EventEmitter类

提供基础的事件功能

  1. on 方法为一个事件创建了监听器。
    • 两个参数
      • 需要监听的事件名称
      • 事件触发时需要调用的函数。
    • s.on('abc', function(){});
  2. emit 方法为触发事件
    • s.emit('abc');

实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//调用util模块,以便调用inherite方法来添加EventEmitter方法添加到我们的Server类中, 事先之前必须安装utils模块 npm install utils
var utils = require('util'), //此处书中有错误,为util模块不是utils模块
EventEmitter = require('events').EventEmitter;

var Server = function(){
console.log('init');
}

utils.inherits(Server, EventEmitter);

var s = new Server();
s.on('abc', function(){
console.log('abc');
});

s.emit('abc');

Callback语法

使用时间很重要的一个部分是处理回掉函数。

HTTP

Node.js的核心功能之一就是Web服务器。

HTTP服务器

使用require调用http模块
使用createServer创建服务器
并使用.listen监听指定端口
还可以使用 request事件监听器,通过EventEmitter调用on方法来完成

1
2
3
4
5
6
7
8
var http = require('http');
var server = http.createServer();
var handleReq = function(req, res){
res.writeHead(200,{});
res.end("hello world");
};
server.on('request', handleReq);
server.listen(4000);

HTTP客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//向百度的80端口发起HTTP GET请求
var http = require('http');
var opts = {
host: "www.baidu.com",
port: 80,
path: '/',
method: 'GET'
};

var req = http.request(opts, function(res){
console.log(res);
res.on('data', function(data){
console.log(data);
});
});
req.end();

opts 配置对象,这里我们必须提供的有

  • host
  • port
  • path
  • method(可选)

使用opts创建一个http.request实例req, 传入的回调函数监听response时间,并处理request的数据,需要注意的是需要使用end()来结束请求。

  1. 提交HTTP GET请求

GET是很常见的HTTP使用方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
var http = require('http');
var opts = {
host: "www.baidu.com",
port: 80,
path: '/'
};

var req = http.get(opts, function(res){
console.log(res);
res.on('data', function(data){
console.log(data);
})
})

去掉了最先的opts里面的method及req.end(),因为这些都已经隐含说明了。
输出指定编码格式的输出。一般使用response.setEncoding()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
var http = require('http');
var opts = {
host: "www.baidu.com",
port: 80,
path: '/'
};

var req = http.get(opts, function(res){
res.setEncoding('utf8');
res.on('data', function(data){
console.log(data);
})
})

这样输出出来的就是转码后的内容。

  1. 发送HTTTP POST 和 PUT 数据

使用POST和PUT可以发送数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var http = require('http');
var options = {
host: "www.example.com",
port: 80,
path: '/submit',
method: 'POST'
};

var req = http.request(options, function(res){
res.setEcoding('utf8');
res.on('data', function(chunk){
console.log('BODY: ' + chunk);
});
});

req.write("my data");
req.write("more of my data");

req.end();

使用http.ClientRequest.write()方法,发送上行数据流,服务器在ClientRequest.end()调用之前是不会响应你的数据请求。

  1. ClientResponse对象

保存关于请求的许多信息。

  • statusCode(HTTP状态)
  • header属性(响应对象头)

URL

URL模块提供了解析和处理URL字符串的便利工具。

parse

1
2
3
4
5
var URL = require('url');
var myUrl = "https://www.baidu.com/s?wd=javascript%20paseInt&rsv_spt=1&rsv_iqid=0xf6e11e8100009e2d&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&oq=javascript%2520%25E5%25AF%25B9%25E8%25B1%25A1&rsv_t=af72%2FXai8pFlwVfzXIUUTPL9cP8%2BsfctlGCXpHeKMTsXzgFgWCGL2wdTA3vEP5QV1vXe&sug=javascript%2520seekto&inputT=4875&rsv_sug3=32&rsv_sug1=15&rsv_sug7=000&rsv_pq=819bc35f000092fd&rsv_sug2=0&rsv_sug4=5270&rsv_sug=1";

parsedUrl = URL.parse(myUrl);
console.log(parsedUrl);
  • 先导入URL模块
  • 创建URL
  • 对该URL使用parse方法。
    • href 完整url
    • protocol 协议https
    • host 服务器名
    • auth 用户证书
    • hostname 主机名
    • port 端口
    • pathname 文件路径
    • search 包含?的部分
    • query 包含?的部分,但删除掉了?
    • hash #之后的部分

parse有两个参数

  • url字符串
  • 布尔值 是否使用queryString模块来解析,默认为false

querystring

querystring使用来处理query字符串的简单辅助模块。

parse

1
2
3
4
5
var qs = require('querystring');
var myUrl = "https://www.baidu.com/s?wd=javascript%20paseInt&rsv_spt=1&rsv_iqid=0xf6e11e8100009e2d&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&oq=javascript%2520%25E5%25AF%25B9%25E8%25B1%25A1&rsv_t=af72%2FXai8pFlwVfzXIUUTPL9cP8%2BsfctlGCXpHeKMTsXzgFgWCGL2wdTA3vEP5QV1vXe&sug=javascript%2520seekto&inputT=4875&rsv_sug3=32&rsv_sug1=15&rsv_sug7=000&rsv_pq=819bc35f000092fd&rsv_sug2=0&rsv_sug4=5270&rsv_sug=1";

parsedUrl = qs.parse(myUrl);
console.log(parsedUrl);

parse方法把query字符串转换成为一个对象,其中属性是对应query字符串中的关键字和变量值。

encode

将parse转换的对象反转为query字符串的格式

1
2
3
var myObj = {'a':1, 'b':5, 'c': 'cats'};
qs.encode(myObj);
'a=1&b=5&c=cats'

I/O

数据流(stream)

可读的数据流

1
2
3
4
var fs = require('fs');
var filehandle = fs.readFile('node_01.js', function(err, data){
console.log(data);
})

文件系统

尽量使用异步对文件进行操作
通过嵌入回调函数完成一部都去并删除文件

1
2
3
4
5
var fs = require('fs');
fs.readFile('shachu.txt', function(e, data){
console.log("war and peace:" + data);
fs.unlink('shachu.txt');
})

Buffer

创建Buffer

  • 三种参数
    • Buffer的字节长度 *
    • 需要拷贝到Buffer里的字节数组
    • 需要拷贝到Buffer里面的字符串 *
1
new Buffer('string', 'ascii'|'utf8')

Buffer.write()会把字符串写到Buffer指定的位置上。

1
2
3
4
5
6
7
8
9
10
> var b = new Buffer(5)
undefined
> b.write('fffff')
5
> b
<Buffer 66 66 66 66 66>
> b.write('ab',1)
2
> b
<Buffer 66 61 62 66 66>

第5章 工具类API

DNS

DNS模块包含了两个主要方法,以及一些便利方法。

  • resolve() 把域名转换成DNS记录
    • 待解析的域名字符串 www.yahoo.com
    • 表示请求的记录类型的字符串 A | CNAME | MX
    • 回调函数
1
2
3
4
5
6
7
8
9
10
var dns = require('dns');

dns.resolve('yahoo.com', 'A', function(e,r){
if(e){
console.log(e);
}
console.log(r);
});

=>[ '98.137.246.7', '98.138.219.232', '98.138.219.231' ]

也可以使用 resolveMX('domain', callback)
如果想只返回一个IP地址的话,使用 dns.lookup()

  • reverse() IP地址转换成域名

加密

Hashing

哈希常用于数据混淆验证。
以下为常见算法
md5
sha1
sha256
sha512
ripemd160

用Hash创建摘要

1
2
3
4
5
var crypto = require('crypto');
var md5 = crypto.createHash('md5');
md5.update('foo');

console.log(md5.digest('hex'));

在digest()传入参数’hex’,将摘要转为十六进制打印,Hash对象最终确认后,不能被重用。

HMAC

结合了哈希算法和加密密钥

crypto.createHmac()创建一个Hmac实例
提供了update()和digest()方法(与hash相同)

1
2
3
4
5
6
7
8
9
var crypto = require('crypto');
var fs = require('fs');

var pem = fs.readFileSync('key.pem'); //之前创建好的key.pem文件,参考openssl创建证书方法
var key = pem.toString('ascii');

var hmac = crypto.createHmac('sha1', key);
hmac.update('foo');
hmac.digest('hex');

进程

process模块

process模块是全局的,并且可以一直通过变量process获得

  1. process事件

    • process是EventEmitter的实例,所以它提供了基于Node进程的系统调用事件

      1
      2
      3
      4
      5
      6
      7
      process.on('exit', function(){
      setTimeout(function(){
      console.log('This will not run'); //此处将不运行
      }, 100);

      console.log('Bye');
      })
    • 利用uncaughtException 事件捕获异常

      用uncaughtException创建一个事件监听器。它能够简单的将异常输出到标准输出。并不影响已经执行的代码将继续。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      var http = require('http');
      var server = http.createServer(function(req, res){
      res.writeHead(200, {});
      res.end('response');
      badLoggingCall('sent response');
      console.log('sent response');
      });

      process.on('uncaughtException', function(e){
      console.log(e);
      });

      server.listen(8080);

      此处将一直显示错误。badLoggingCall is not defined,但服务器并没有关闭退出

  2. 与当前的Node进行交互