Node即学即用 第1章 Node.js简介 开始写代码
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环境
编写首个服务器程序
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);
增加了对退出用户的删除,错误记录。大家可以慢慢研究
此处使用Express模块,先创建项目目录,进入目录后输入以下命令
1 npm install --save express
安装完成后我们就可以继续了。首先创建件的Express服务器
创建服务器 1 2 3 4 5 6 7 8 9 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去处理
模式
无序的并行I/O Node中所有的I/O操作默认都是无序并行,无序并非乱序,是指顺序无法保证。
顺序串行I/O 每一个人物都必须在上一个任务完成后才能开始。
编写产品代码 编写成熟的代码,是程序更容易维护,更简洁,更易懂。
差错处理 在Node.js中使用error时间来处理异步错误
req.on('error', function(e){...})
使用多处理器 Node.js 可以使用 cluster模块来把任务分配给子进程
第4章 核心API Events EventEmitter类 提供基础的事件功能
on 方法为一个事件创建了监听器。
两个参数
s.on('abc', function(){});
emit 方法为触发事件
实例代码
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()来结束请求。
提交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); }) })
这样输出出来的就是转码后的内容。
发送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()调用之前是不会响应你的数据请求。
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()
加密 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获得
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
,但服务器并没有关闭退出
与当前的Node进行交互