nodejs
1.notejs表单就数据解析
2.notejs表单数据提取
3.notejs代码分多个
4.notejs实时数据通讯
5.多人实时聊天服务器
Node.js特点:单线程、异步I/O(非阻塞I/O)、事件驱动(事件环)
适合的程序:就是没有太多的计算,I/O比较多的业务。
举例:留言本、考试系统、说说、图片裁切服务器
Node.js原生: http、fs、path、url。 静态服务、简单路由、GET、POST请求。
模块:formidable、gm、express
Express:中间件、MVC建站、模板引擎ejs、静态服务、简单路由、GET、POST请求、MD5加密、图片上传。
服务器的一些概念:Cookie(对于没有上传服务器的,本地存储)、Session(做了一个密钥)
持久化NoSQL: 非关系型数据库,Not Only SQL。
特点:没有schema,没有行和列。用文档(JSON)来存储。
MongoDB:安装、开启、导入数据、Shell管理数据库、Mongo Vue、Node.js做CRUD(增删改查)、DAO层的封装、索引、操作符$set $lt $gt $push $pull
Mongoose: ODM,不用直接操作数据库,操作对象,这个对象自动持久。
1.Node.js概念
JavaScript 显示方案
JavaScript 能够以不同方式“显示”数据:
- 使用 window.alert() 写入警告框
- 使用 document.write() 写入 HTML 输出
- 使用 innerHTML 写入 HTML 元素
- 使用 console.log() 写入浏览器控制台
Node.js是一个让JavaScript运行在服务器端的开发平台
1.关于 Node.js
Node.js是一个让JavaScript运行在服务器端的开发平台.
1.1 介绍
-
node.js 不是一种独立的语言,与PHP,JSP,PYTHON,PERL,RUBY的既是语言,也是平台不同,node.js使用javascript进行编成,运行在javascript引擎上(V8)
-
与PHP,JSP等相比,Nodejs跳过了Apache,Nginx,ISS等HTTP服务器,它自己不用建设在任何服务器软件之上.nodejs的许多谁就理念与经典架构LANP有着很大的不同,可以提供强大的伸缩能力
linux apache/nginx mysql php
linux nodejs mongodb mysql nodejs本身就是服务器 js作为后端语言 ~~~
-
php 开发web
html css js php mysql apache 数据:array
node 开发web
html css js node mongodb/mysql 数据:json
1.2特点
-
单线程
- 所有用户端请求的连接 都是用一个线程来处理
- nodejs 不是为每个用户 创建一个新的连接,而是仅仅使用一个线程
- 单线程带来的好处:操作系统不会为新线程创建 ;销毁 内容空间,而用额外资源
-
非阻塞I/O
- I/O操作不会阻塞程序的运行
-
事件驱动
- 客户端请求建立连接,提交数据等行为,就会触发 相应的时间
- 在node中,在一个时刻,只能执行 一个时间回调函数,但在执行 一个时间回调函数的中途,可以转而处理其他事件(比如,又有新用户连接了),然后返回继续执行原事件的回调函数,这种处理机制,称为”事件环”机制
nodejs底层是c++(V8引擎也是C++写的),底层代码中,近半数都用于事件队列、回调函数队列的构建,用事件驱动来完成服务器的 任务调度,是nodejs中 真正底层核心逻辑
-
三特点说明
- 单线程
- 是为了 减少内存的消耗,OS的内存
- 但是,如果每个请求都有I/O,单线程就会 被 阻塞
- 非阻塞I/O
- 不会傻等着 I/O操作完成后,才去执行后面的操作,而会 直接执行后面的语句
- 非阻塞I/O的操作,一个操作还未完成,下一个操作又来了,这么使用就是事件环
- 事件驱动(事件环):
- 不管新用户的请求还是老用户的请求(I/O操作),都将以事件的方式加入,事件环,等待调度
- nodejs所有的I/O 都是异步的,回调函数陶俑回调函数
- 单线程
1.3nodejs的有缺点,适合什么开发
优点:
1) 善于I/O,不善于计算
2)处理高并发
3)服务器推送
缺点:
1)单一线程,一旦崩溃,整个服务就挂了
超人死了(只有一个人做事),世界都末日了;服务不是绝对可靠的;
1.4使用场景
不能完全替代 传统的后端语言,但在某些方面优于传统.
当应用程序需要处理大量并发的I/O操作,而在发出响应之前,
应用程序内部 并不需要进行非常复杂的计算处理的时候,Node.js非常适合。
Node.js也非常适合与web socket配合,开发长连接的实时交互应用程序。
- 聊天室
- 图文直播
- 考试系统
- 收集用户数据的表单
- 提供JSON的API
###
使用了nodejs的express,ejs,fs模块
2.搭建服务
2.3 )搭建nodejs服务器 – 首个 nodejs页面
nodejs是服务端程序,写好js,运行在服务器上,
返回给客户端的是什么?=>已经处理好的结果
如果修改了程序,必须重启服务,才能查看修改,
我们任何一个js文件 必须通过node来运行
2.4)根目录与web容器
1)nodejs没有根目录的概念
因为它根本没有任何的web容器!
2)静态页面的呈现
fs模块Readfile
URL和真实的物理文件之间是没有任何关系的 URL是通过Node的路由设计之后,才呈现出某一个静态文件
3)HTTP运行原理
- html
- css
- js
- img
- 由此,我们可以看出,要是使用nodejs 搭建服务器, 是需要写 各种回调函数,定义 各种路由规则,来实现 页面的显示.
3)HTTP模块
nodejs将各种功能,都划分为了一个个module(模块)
需要用什么模块,就可以使用require(‘‘)来引入使用
4)URL模块
4.1. url信息
4.2. 路由的使用
/stu/20170505001 查询学员信息
/tch/00001 查询员工信息
5)文件系统
5.1. 新建文件夹 / 删除文件夹 / 文件状态信息 / 读取文件夹 5.2. 读取出 文件夹/文件
var fs = require('fs') //引入文件模块
fs.mkdir('./pics/aaa', function(err){ //创建文件夹
console.log(err);
res.end('文件夹创建成功')
})
fs.rmdir('./pics/aaa',function(err, stats){ //文件状态信息
console.log(stats.isFile());
console.log(stats.idDirectory());
res.end('检测完成');
})
fs.readdir( './imgs', function(err, files){ //读取文件信息
//以数组的形式,返回该文件夹的文件名
console.log(files);
res.end('读取完成');
}
);
6.)静态目录
文件加载 (web容器)
7.)模块的概念
在Node.js中,不可能用一个js文件去写全部的业务,肯定要有MVC. 它以模块为单位 划分所有功能,并且提供了一个 完整的模块加载机制,我们可以将应用程序 划分为各个不同的部分.
每一个JavaScript文件都是一个模块;而多个JavaScript文件可以使用require引入,使他们共同实现了一个功能模块.
7.1 输出变量/函数
Node.js中,JS文件中定义的变量、函数,都只在这个文件内部有效. 其他文件中需要引用变量、函数时,必须使用exports对象
进行暴露(输出).使用者要用require()
命令,引用执行这个JS文件.
7.2 输出一个类(构造函数)
可以用module.exports = 构造函数名;
的方式 向外输出一个类
7.3 模块关联关系
a. 某一个js文件中,有函数或变量: exports.变量 = 变量;
b. 某一个js文件中,有一个类: module.exports = 构造函数名;
7.4 模块封装
8).npm (node package management)
这是一个工具名字.npm的主要职责是 安装开发包和管理依赖项. 安装开发包:安装 npm install命令;更新 npm update命令. 管理依赖项:借助package.json文件;最简单生成package.json的方法就是npm init
npm不需要单独安装,只要安装了Node.js环境,npm就已经包含在里面了. 查看 npm: npm -v
为什么要使用npm?
开发时,会使用到各种功能的组件,所有组建都由我们自己来写代码的花,开发效率就会很低,哦们不要重复的去造轮子,要学会使用已有的工具来完善我们的项目,站在巨人的肩膀上工作.
npm是js世界里的一个伟大的社区,能够让开发者更加轻松的共享代码和共用代码片段或模块组件.
不要修改[node_modules]+package-lock.json这两个文件,因为它是使用npm去管理的
9. POST请求
相比较GET请求,POST请求比较复杂。
因为Node.js认为,使用POST请求时,数据量会比较多,为了追求效率,他将数据拆分了众多小的数据块(chunk),然后通过特定的事件,将这些小数据块有序传递给回调函数
10. 文件上传处理
原生写POST处理,比较复杂,要写两个监听.
文件上传业务比较麻烦,所以,用第三方模块:formidable
11. ejs模板
ejs包 ejs是Embedded JavaScript templates的简称,意思是嵌入式JavaScript模板.node中的后台模版.
使用: 直接npm 安装即可npm insatll ejs –save
使用淘宝 NPM 镜像
大家都知道国内直接使用 npm 的官方镜像是非常慢的,这里推荐使用淘宝 NPM 镜像。 淘宝 NPM 镜像是一个完整 npmjs.org 镜像,你可以用此代替官方版本(只读),同步频率目前为 10分钟 一次以保证尽量与官方服务同步。
你可以使用淘宝定制的 cnpm (gzip 压缩支持) 命令行工具代替默认的 npm: $ npm install -g cnpm –registry=https://registry.npm.taobao.org 这样就可以使用 cnpm 命令来安装模块了: $ cnpm install [name]
注意:(补充)例子
PS1. V8引擎说明 V8 JavaScript引擎是 Google用于其Chrome浏览器的底层JavaScript引擎。Google使用V8创建了一个用C++编写的超快解释器,该解释器拥有另一个独特特征:您可以下载该引擎并将其嵌入任何应用程序。V8 JavaScript引擎并不仅限于在一个浏览器中运行。
因此,Node.js实际上会使用Google编写的V8 JavaScript引擎,并将其重建为可在服务器上使用。
PS2. 沏茶: 洗茶壶4,洗茶杯3,烧开水8,准备茶叶2,泡茶水5,倒茶水1. 14 / 15 4+8+5+1=18
PS3. 视频
PS4. 餐馆例子
PS5. PS: JS随机数的产生.
Math.random()函数返回0和1之间的伪随机数,可能为0,但总是小于1,(0,1)
生成n-m,包含n 但不包含m 的整数:
1. 算出 m-n的值,假设等于w
2. Math.random()*w
3. Math.random()*w+n
4. parseInt(Math.random()*w+n, 10)
生成n-m,不包含n 但包含m 的整数:
1. 算出 m-n的值,假设等于w
2. Math.random()*w
3. Math.random()*w+n
4. Math.floor(Math.random()*w+n) + 1
生成n-m,不包含n和m的整数:
1. 算出 m-n-2的值,假设等于w
2. Math.random()*w
3. Math.random()*w+n +1
4. Math.round(Math.random()*w+n+1) 或者 Math.ceil(Math.random()*w+n+1)
生成n-m,包含n和m的随机数:
1. 算出 m-n的值,假设等于w
2. Math.random()*w
3. Math.random()*w+n
4. Math.round(Math.random()*w+n) 或者 Math.ceil(Math.random()*w+n)
Express 表达
1.Express框架
基于 Node.js 平台,快速、开放、极简的 web 开发框架。
它是用于后台NodeJs的框架,与JQuery/Bootstrap/vue.js/AngularJs这类前端框架是不一样的! Express 不对 Node.js 已有的特性进行二次抽象,
什么是二次抽象?
我们只是在它之上扩展了 Web 应用所需的基本功能。丰富的 HTTP 快捷方法和任意排列组合的 Connect 中间件,让你创建健壮、友好的 API 变得既快速又简单。
2.对比原生Node.js
使用原生Node.js开发,会发现有很多问题: - 呈现静态页面很不方便,需要处理每个HTTP请求,还要考虑304缓存问题. - 路由处理代码不直观清晰,需要写很多正则表达式和字符串函数. - 开发者不能集中精力写业务,要考虑很多其他的东西.
使用原生Node.js开发,会发现很多问题:
呈现静态页面很不方便,需要处理每个HTTP请求,还要考虑304缓存问题
路由处理代码不直观清晰,需要写很多正则表达式和字符串函数
开发者不能集中精力写业务,要考虑很多的东西
安装
npm install express –save #即可安装最新版本的Express
- Express4.x与3.x版本的差别非常大,我们使用4.x
- 基本使用
- 路由处理
- 对模板引擎的支持
- 静态文件/目录处理
request 和 response 对象的具体介绍:
Request 对象 - request 对象表示 HTTP 请求,包含了请求查询字符串,参数,内容,HTTP 头部等属性。常见属性有:
- req.app:当callback为外部文件时,用req.app访问express的实例
- req.baseUrl:获取路由当前安装的URL路径
- req.body / req.cookies:获得「请求主体」/ Cookies
- req.fresh / req.stale:判断请求是否还「新鲜」
- req.hostname / req.ip:获取主机名和IP地址
- req.originalUrl:获取原始请求URL
- req.params:获取路由的parameters
- req.path:获取请求路径
- req.protocol:获取协议类型
- req.query:获取URL的查询参数串
- req.route:获取当前匹配的路由
- req.subdomains:获取子域名
- req.accepts():检查可接受的请求的文档类型
- req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages:返回指定字符集的第一个可接受字符编码
- req.get():获取指定的HTTP请求头
- req.is():判断请求头Content-Type的MIME类型
Response 对象 - response 对象表示 HTTP 响应,即在接收到请求时向客户端发送的 HTTP 响应数据。常见属性有:
- res.app:同req.app一样
- res.append():追加指定HTTP头
- res.set()在res.append()后将重置之前设置的头
- res.cookie(name,value [,option]):设置Cookie
- opition: domain / expires / httpOnly / maxAge / path / secure / signed
- res.clearCookie():清除Cookie
- res.download():传送指定路径的文件
- res.get():返回指定的HTTP头
- res.json():传送JSON响应
- res.jsonp():传送JSONP响应
- res.location():只设置响应的Location HTTP头,不设置状态码或者close response
- res.redirect():设置响应的Location HTTP头,并且设置状态码302
- res.render(view,[locals],callback):渲染一个view,同时向callback传递渲染后的字符串,如果在渲染过程中有错误发生next(err)将会被自动调用。callback将会被传入一个可能发生的错误以及渲染后的页面,这样就不会自动输出了。
- res.send():传送HTTP响应
- res.sendFile(path [,options] [,fn]):传送指定路径的文件 -会自动根据文件extension设定Content-Type
- res.set():设置HTTP头,传入object可以一次设置多个头
- res.status():设置HTTP状态码
- res.type():设置Content-Type的MIME类型
3.路由
3.1. 路由的访问方式
3.2. 路由路径
3.3. 响应方法
res.download() 提示下载文件。
res.end() 终结响应处理流程。
res.json() 发送一个JSON格式的响应。
res.jsonp() 发送一个支持JSONP的JSON格式的响应
res.redirect() 重定向请求。
res.render() 渲染视图模板。
res.send() 发送各种类型的响应。
res.sendFile() 以八位字节流的形式发送文件。
res.sendStatus() 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。
4.中间件
4.1 中间件概念
4.2express路由具有的中间件特性
4.3app.use()的特性
app.use()就是一个中间件,与get()/pot() 方法不同的是他的URL不是精确匹配,而是模糊匹配
4.4 app.use()使用
4.5静态资源服务的说明
4.6 404页面的说明
5.模板引擎,渲染页面
5.1模板引擎的设置和使用
5.2原声的end()与express的send()的区别
5.3设置响应头 和 状态码 和MIME类型
6.GET 与 POST
6.1 GET
原生node中,要想得到get参数,需要借助于url模块来识别参数字符串
在Express中,不需要使用url模块了,可以直接使用req.query
对象得到GET参数
6.2 POST
GET请求的参数是隐蔽传参(在请求体中)
POST请求在Express中不能直接获得,必须使用body-parser
模块,使用后,将可以用req.body得到参数
但是如果表单中含有文件上传,那么还是需要使用formidable
模块
7.Express对数据库的支持
7.1数据库集成
7.2连接mysql数据库
7.3使用数据库 -查/增/改/删
7.4连接池
相册实例 [images] |– controller |– models |– views |– public |– uploads |– node_modules index.js 运行文件
初始化项目
npm init
将项目名称设置为: images | 运行文件 index.js
安装框架/模块
npm install express --save
npm install ejs --save
npm install formidable --save
npm install body-parser --save
npm install silly-datetime --save
npm install random-int --save
Net 模块
Node.js Net 模块提供了一些用于底层的网络通信的小工具,包含了创建服务器/客户端的方法,我们可以通过以下方式引入该模块:
var net = require("net")
方法
序号 | 方法 & 描述 |
---|---|
1 | net.createServer([options][, connectionListener]) 创建一个 TCP 服务器。参数 connectionListener 自动给 ‘connection’ 事件创建监听器。 |
2 | net.connect(options[, connectionListener]) 返回一个新的 ‘net.Socket’,并连接到指定的地址和端口。 当 socket 建立的时候,将会触发 ‘connect’ 事件。 |
3 | net.createConnection(options[, connectionListener]) 创建一个到端口 port 和 主机 host的 TCP 连接。 host 默认为 ‘localhost’。 |
4 | net.connect(port[, host][, connectListener]) 创建一个端口为 port 和主机为 host的 TCP 连接 。host 默认为 ‘localhost’。参数 connectListener 将会作为监听器添加到 ‘connect’ 事件。返回 ‘net.Socket’。 |
5 | net.createConnection(port[, host][, connectListener]) 创建一个端口为 port 和主机为 host的 TCP 连接 。host 默认为 ‘localhost’。参数 connectListener 将会作为监听器添加到 ‘connect’ 事件。返回 ‘net.Socket’。 |
6 | net.connect(path[, connectListener]) 创建连接到 path 的 unix socket 。参数 connectListener 将会作为监听器添加到 ‘connect’ 事件上。返回 ‘net.Socket’。 |
7 | net.createConnection(path[, connectListener]) 创建连接到 path 的 unix socket 。参数 connectListener 将会作为监听器添加到 ‘connect’ 事件。返回 ‘net.Socket’。 |
8 | net.isIP(input) 检测输入的是否为 IP 地址。 IPV4 返回 4, IPV6 返回 6,其他情况返回 0。 |
9 | net.isIPv4(input) 如果输入的地址为 IPV4, 返回 true,否则返回 false。 |
10 | net.isIPv6(input) 如果输入的地址为 IPV6, 返回 true,否则返回 false。 |
net.Server
net.Server通常用于创建一个 TCP 或本地服务器。
序号 | 方法 & 描述 |
---|---|
1 | server.listen(port[, host][, backlog][, callback]) 监听指定端口 port 和 主机 host ac连接。 默认情况下 host 接受任何 IPv4 地址(INADDR_ANY)的直接连接。端口 port 为 0 时,则会分配一个随机端口。 |
2 | server.listen(path[, callback]) 通过指定 path 的连接,启动一个本地 socket 服务器。 |
3 | server.listen(handle[, callback]) 通过指定句柄连接。 |
4 | server.listen(options[, callback]) options 的属性:端口 port, 主机 host, 和 backlog, 以及可选参数 callback 函数, 他们在一起调用server.listen(port, [host], [backlog], [callback])。还有,参数 path 可以用来指定 UNIX socket。 |
5 | server.close([callback]) 服务器停止接收新的连接,保持现有连接。这是异步函数,当所有连接结束的时候服务器会关闭,并会触发 ‘close’ 事件。 |
6 | server.address() 操作系统返回绑定的地址,协议族名和服务器端口。 |
7 | server.unref() 如果这是事件系统中唯一一个活动的服务器,调用 unref 将允许程序退出。 |
8 | server.ref() 与 unref 相反,如果这是唯一的服务器,在之前被 unref 了的服务器上调用 ref 将不会让程序退出(默认行为)。如果服务器已经被 ref,则再次调用 ref 并不会产生影响。 |
9 | server.getConnections(callback) 异步获取服务器当前活跃连接的数量。当 socket 发送给子进程后才有效;回调函数有 2 个参数 err 和 count。 |
事件
序号 | 事件 & 描述 |
---|---|
1 | listening 当服务器调用 server.listen 绑定后会触发。 |
2 | connection 当新连接创建后会被触发。socket 是 net.Socket实例。 |
3 | close 服务器关闭时会触发。注意,如果存在连接,这个事件不会被触发直到所有的连接关闭。 |
4 | error 发生错误时触发。’close’ 事件将被下列事件直接调用。 |
net.Socket
net.Socket 对象是 TCP 或 UNIX Socket 的抽象。net.Socket 实例实现了一个双工流接口。 他们可以在用户创建客户端(使用 connect())时使用, 或者由 Node 创建它们,并通过 connection 服务器事件传递给用户。
事件
net.Socket 事件有:
序号 | 事件 & 描述 |
---|---|
1 | lookup 在解析域名后,但在连接前,触发这个事件。对 UNIX sokcet 不适用。 |
2 | connect 成功建立 socket 连接时触发。 |
3 | data 当接收到数据时触发。 |
4 | end 当 socket 另一端发送 FIN 包时,触发该事件。 |
5 | timeout 当 socket 空闲超时时触发,仅是表明 socket 已经空闲。用户必须手动关闭连接。 |
6 | drain 当写缓存为空得时候触发。可用来控制上传。 |
7 | error 错误发生时触发。 |
8 | close 当 socket 完全关闭时触发。参数 had_error 是布尔值,它表示是否因为传输错误导致 socket 关闭。 |
属性
net.Socket 提供了很多有用的属性,便于控制 socket 交互:
序号 | 属性 & 描述 |
---|---|
1 | socket.bufferSize 该属性显示了要写入缓冲区的字节数。 |
2 | socket.remoteAddress 远程的 IP 地址字符串,例如:’74.125.127.100’ or ‘2001:4860:a005::68’。 |
3 | socket.remoteFamily 远程IP协议族字符串,比如 ‘IPv4’ or ‘IPv6’。 |
4 | socket.remotePort 远程端口,数字表示,例如:80 or 21。 |
5 | socket.localAddress 网络连接绑定的本地接口 远程客户端正在连接的本地 IP 地址,字符串表示。例如,如果你在监听’0.0.0.0’而客户端连接在’192.168.1.1’,这个值就会是 ‘192.168.1.1’。 |
6 | socket.localPort 本地端口地址,数字表示。例如:80 or 21。 |
7 | socket.bytesRead 接收到得字节数。 |
8 | socket.bytesWritten 发送的字节数。 |
方法
序号 | 方法 & 描述 |
---|---|
1 | new net.Socket([options]) 构造一个新的 socket 对象。 |
2 | socket.connect(port[, host][, connectListener]) 指定端口 port 和 主机 host,创建 socket 连接 。参数 host 默认为 localhost。通常情况不需要使用 net.createConnection 打开 socket。只有你实现了自己的 socket 时才会用到。 |
3 | socket.connect(path[, connectListener]) 打开指定路径的 unix socket。通常情况不需要使用 net.createConnection 打开 socket。只有你实现了自己的 socket 时才会用到。 |
4 | socket.setEncoding([encoding]) 设置编码 |
5 | socket.write(data[, encoding][, callback]) 在 socket 上发送数据。第二个参数指定了字符串的编码,默认是 UTF8 编码。 |
6 | socket.end([data][, encoding]) 半关闭 socket。例如,它发送一个 FIN 包。可能服务器仍在发送数据。 |
7 | socket.destroy() 确保没有 I/O 活动在这个套接字上。只有在错误发生情况下才需要。(处理错误等等)。 |
8 | socket.pause() 暂停读取数据。就是说,不会再触发 data 事件。对于控制上传非常有用。 |
9 | socket.resume() 调用 pause() 后想恢复读取数据。 |
10 | socket.setTimeout(timeout[, callback]) socket 闲置时间超过 timeout 毫秒后 ,将 socket 设置为超时。 |
11 | socket.setNoDelay([noDelay]) 禁用纳格(Nagle)算法。默认情况下 TCP 连接使用纳格算法,在发送前他们会缓冲数据。将 noDelay 设置为 true 将会在调用 socket.write() 时立即发送数据。noDelay 默认值为 true。 |
12 | socket.setKeepAlive([enable][, initialDelay]) 禁用/启用长连接功能,并在发送第一个在闲置 socket 上的长连接 probe 之前,可选地设定初始延时。默认为 false。 设定 initialDelay (毫秒),来设定收到的最后一个数据包和第一个长连接probe之间的延时。将 initialDelay 设为0,将会保留默认(或者之前)的值。默认值为0. |
13 | socket.address() 操作系统返回绑定的地址,协议族名和服务器端口。返回的对象有 3 个属性,比如{ port: 12346, family: ‘IPv4’, address: ‘127.0.0.1’ }。 |
14 | socket.unref() 如果这是事件系统中唯一一个活动的服务器,调用 unref 将允许程序退出。如果服务器已被 unref,则再次调用 unref 并不会产生影响。 |
15 | socket.ref() 与 unref 相反,如果这是唯一的服务器,在之前被 unref 了的服务器上调用 ref 将不会让程序退出(默认行为)。如果服务器已经被 ref,则再次调用 ref 并不会产生影响。 |
实例
创建 server.js 文件,代码如下所示:
var net = require('net');
var server = net.createServer(function(connection) {
console.log('client connected');
connection.on('end', function() {
console.log('客户端关闭连接');
});
connection.write('Hello World!\r\n');
connection.pipe(connection);
});
server.listen(8080, function() {
console.log('server is listening');
});
执行以上服务端代码:
$ node server.js
server is listening # 服务已创建并监听 8080 端口
新开一个窗口,创建 client.js 文件,代码如下所示:
var net = require('net');
var client = net.connect({port: 8080}, function() {
console.log('连接到服务器!');
});
client.on('data', function(data) {
console.log(data.toString());
client.end();
});
client.on('end', function() {
console.log('断开与服务器的连接');
});
执行以上客户端的代码:
连接到服务器!
Hello World!
断开与服务器的连接
Socket.IO框架
Web Socket 和 Socket.IO框架
https://socket.io/docs/ 官网,
https://www.w3cschool.cn/socket/ w3c
8.1 HTTP的问题
HTTP无法轻松实现 实时应用:
-
HTTP协议是无状态的,服务器只会响应来自客户端的请求,但是它与客户端之间不具备持续连接。(无法长时持续连接)
-
我们可以捕获浏览器上发生的事件(比如用户点击了button),这个事件可以产生与服务器的数据交互(比如Ajax)。 但是,反过来却是不可能的:服务器端发生了一个事件,服务器无法将这个事件的信息实时主动通知它的客户端。只有在客户端查询服务器的当前状态的时候,所发生事件的信息才会从服务器传递到客户端。(无法主动输出信息)
但是,HTTP协议也能做聊天室这种’长连接’的东西,它是这么实现的:
-
长轮询:客户端每隔很短的时间,都会对服务器发出请求,查看是否有新的消息,只要轮询速度足够快,例如1秒,就能给人造成交互是实时进行的印象。这种做法是无奈之举,实际上对服务器、客户端双方都造成了大量的性能浪费。
-
长连接:客户端只请求一次,但是服务器会将连接保持,不会返回结果(想象一下我们没有写res.end()时,浏览器的小菊花会一直转)。服务器有了新数据,就将数据发回来,又有了新数据,就将数据发回来,而一直保持挂起状态。这种做法的也造成了大量的性能浪费。
8.2. WebSocket
WebSocket协议能够让浏览器和服务器全双工实时通信,互相的,服务器也能主动通知客户端了。
- WebSocket的原理非常的简单:利用HTTP请求产生握手,HTTP头部中含有WebSocket协议的请求,所以握手之后,二者转用TCP协议进行交流(QQ的协议)。现在的浏览器和服务器之间,就是QQ和QQ服务器的关系了。 所以WebSocket协议,需要浏览器支持,更需要服务器支持。
- 支持WebSocket协议的浏览器有:Chrome 4、火狐4、IE10、Safari5
- 支持WebSocket协议的服务器有:Node、Apache Tomcat/7.0.27、Nginx1.3
8.3. Socket.IO
用原生Node搭建 WebSocket协议的服务 非常麻烦,我们使用写好的模块: Socket.IO 它屏蔽了所有底层细节,让顶层调用非常简单。 并且还为不支持WebSocket协议的浏览器(IE),提供了长轮询的透明模拟机制。 Node的单线程、非阻塞I/O、事件驱动机制,使它非常适合Socket服务器。
电子表.html-work
<!DOCTYPE html>
<html>
<body>
<p id="demo"></p>
<script>
var myVar = setInterval(myTimer, 500);
function myTimer() {
var d = new Date();
document.getElementById("demo").innerHTML = d.toLocaleTimeString();
}
</script>
</body>
</html>
4.代码区
1.迭代器的使用
作用:将异步强变同步;
var http = require('http');
var fs = require('fs');
var port = 3000;
var hostname = '127.0.0.1'
var server = createServer(function(req,res){
// 读取文件夹
fs.readdir('./img',function(err,files)){
console.log(files);
//定义一个数组用于存储files
var getFile = [];
//带函数名的自执行
(function demo(i){
// 添加宗旨条件
if(i == files.length)return;
// 查看文件状态
fs.stat('./img/' +files[i],function(err,stats){
// 判断是否为文件夹
if(stats.isDirectory(files[i]){
getFile.push(files[i]);
});
});
// 递归;
demo(i++);
})(0);
};
});
server.listen();
2. web 容器;
// 1.加载相关模块
// 2. 判断用户有无输入 默认加载index文件;
// 用path模块获取path文件名
// 3. 读取文件
//
// 4.设定mime类型 可以通过加载模块文家获取
var http = require('http');
var fs = require('fs');
var path = require('path');
var port = 3000;
var hostname = '127.0.0.1'
var server = createServer(function(req,res){
// 跳过了 chrome 的收藏夹图标的请求
if (req.url == '/favicon.ico') return;
// 得到用户的路径
var pathname = url.parse(req.url).pathname;
// console.log(pathname);
// 没有指定访问的文件,则默认访问 index
// 没有指定访问的文件,则默认访问 index
if (pathname.indexOf('.') == -1) {
pathname += 'index.html';
}
// 读取访问的文件
fs.readFile('./static/'+pathname, function(err, data){
if (err) {
// 如果文件不存在,就必须显示404
fs.readFile('./static/404.html', function (err, data){
res.writeHead(404, {'content-type': 'text/html;charset=utf-8'});
res.end(data);
});
return; // 停止运行输出
}
var extname = path.extname();
// 因为文件的MIME类型(扩展名)不同,所以头信息的设置 要动态设置才可以
// 获取到 实际访问文件的 后缀, 要根据这个后缀,得到对应的MIME类型
// .html => text/html | .png => image/png | .css
// 响应得到文件的后缀,就需要使用 path 模块
var extname = path.extname(pathname);
// console.log(extname);
/*var mime = getMime(extname);
res.writeHead(200, {'content-type': mime});
res.end(data);*/
/* // 异步执行
getMime(extname, function (mime){
res.writeHead(200, {'content-type': mime});
res.end(data);
});*/
// 使用nodejs提供的同步接口实现读取mime 文件
var mimedata = fs.readFileSync('./mime.json');
var mime = JSON.parse(mimedata);
res.writeHead(200, {'content-type': mime[extname]});
res.end(data);
});
});
// 运行服务器
server.listen(port, hostname);
/*// 传入后缀,返回对应的MIME类型
function getMime(extname, callback){
// 读取mime.json文件,用作于后缀与mime类型的匹配
fs.readFile('./mime.json', function (err, data){
// console.log(data);
var mimeJSON = JSON.parse(data);
// console.log(mimeJSON);
// console.log(mimeJSON[extname]); // 输出mime类型
// return mimeJSON[extname];
callback(mimeJSON[extname]);
});
}*/
/*// 传入后缀,返回对应的MIME类型
function getMime(extname){
// 读取mime.json文件,用作于后缀与mime类型的匹配
fs.readFile('./mime.json', function (err, data){
// console.log(data);
var mimeJSON = JSON.parse(data);
// console.log(mimeJSON);
// console.log(mimeJSON[extname]); // 输出mime类型
return mimeJSON[extname];
});
}*/
/*// 传入后缀,返回对应的MIME类型
function getMime(extname){
switch (extname) {
case '.html': return 'text/html'; break;
case '.jpg': return 'image/jpg'; break;
case '.png': return 'image/png'; break;
case '.css': return 'text/css'; break;
case '.js': return 'application/javascript'; break;
}
}
*/
##
// 异步执行
getMime(extname, function (mime){ #function相当于回调函数也相当于匿名函数定义,只有被调用才会i执行
res.writeHead(200, {'content-type': mime}); #-------------------01
res.end(data);
});
/*// 传入后缀,返回对应的MIME类型
function getMime(extname, callback){ #-------------------02
// 读取mime.json文件,用作于后缀与mime类型的匹配 # ####这里的callback参数其实就是为了接调用时传入的回调函数而准备的。
fs.readFile('./mime.json', function (err, data){
// console.log(data);
var mimeJSON = JSON.parse(data);
// console.log(mimeJSON);
// console.log(mimeJSON[extname]); // 输出mime类型 # ---------------03
// return mimeJSON[extname];
callback(mimeJSON[extname]); #-------------------04
#这里的callback();相当于 调用时 func回调函数;三
});
}*/
//异步执行的思路:
/*
安正常的思路来说,我们的程序在加载完后会出现,异步还没加载完就已经程序执行完的操作了 这时我们就得用以上方法,将异步的函数换过来;
思路:
首先我们调用01函数; 遇到function跳过(没有被调用所以不执行)
在执行getMime时,遇到callback回调函数时,我们会跳过他执行下面的fs.readFile函数,我们的参数extname在上面就已经获取到了 因此我们可以执行fs.readFile();函数
接着,只有执行完03时(回调函数的特点),我们才开始执行04;并且将获取到的参数传入callback中;
其实callback()函数就是我们调用getMime时function()函数;因此;我们调用他
我们把mimeJSON【extname】 参数传到callback中,执行01的function();然后就显示出页面;
01(调用)-> 02(只有执行完02才允许执行01的回调函数func) -> 03 ->04 ->01;
*/
3. 获取IP地址;
var http = require('http');
var fs = require('fs');
var hostname = '127.0.0.1';
// var hostname = '192.168.14.254';
var port = 3000;
console.log(1);
// 创建服务器
var server = http.createServer(function(req, res){
// 跳过了 chrome 的收藏夹图标的请求
if (req.url == '/favicon.ico') return;
// 获取用户的IP
var userIp = getIp(req);
// 随机数 1-9
var num = Math.ceil(Math.random() * 10000 % 9);
// console.log('欢迎IP: ' + userIp + ' 的用户读取第:[ ' + num + ' ]张图片');
// 读取图片输出
fs.readFile('./imgs/'+num+'.jpg', function (err, data){
if (err) throw err;
res.writeHead(200, {'content-type': 'image/jpeg'});
// console.log(userIp + ' 的第[ ' + num + ' ]张图片,已读取完成!');
res.end(data);
console.log(2);
});
console.log(3);
});
// 运行服务器
server.listen(port, hostname);
console.log(4);
// 获取url请求客户端ip
var getIp = function(req) {
var ip = req.headers['x-forwarded-for'] ||
req.ip ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress || '';
if(ip.split(',').length>0){
ip = ip.split(',')[0]
}
return ip;
};
5.NODEJS聊天室
小结:
1.用html+jquery+nodejs+socket实时返回图形
2.用exports实施nodejs方法分布引用
1.切换国内源(乌班图)
npm install -g cnpm –registry=https://registry.npm.taobao.org
2.安装需要软件包
这里需要安装2个软件包
-
cnpm install socket.io
-
cnpm install express ~~~
3.聊天室服务器端代码
var app = require("express")(); //加载express模块,用于管理路由
var http = require("http").createServer(app); //加载http服务器模块
var io = require("socket.io")(http); //加载socket.io
//处理根请求时,加载index.html文件
app.get("/",function(req,res){
res.sendFile(__dirname+"/index.html");
});
//当客户端有连接时
io.on('connection',function(socket){
//当客户端失去连接时
socket.on("disconnect",function(){
console.log("a user leave");
});
//当服务器受到请求时
socket.on("chat msg",function(msg){
//向所有连接客户端发送广播
socket.broadcast.emit("chat msg",msg);
});
});
//启动服务
http.listen(3001,()=>{
console.log("server start on 127.0.0.1:3001");
});
4.客户端代码
<html>
<head>
<style>
.msgbox{
width:500px;
height:400px;
background:#eee;
margin:10px auto;
}
.btn{
width:50px;
height:28px
}
.msg{
width:450px;
height:30px;
}
.inputbox{
width:500px;
height:30px;
margin:10px auto;
}
#showMsg{
list-style:none;
margin:0px;
padding:0px;
}
#showMsg li{
height:20px;
padding:2px;
}
</style>
</head>
<body>
<div class="msgbox">
<ul id="showMsg"></ul>
</div>
<div class="inputbox">
<form action="">
<input type="text" class="msg"><button class="btn">发送</button>
</form>
</div>
</body>
<script src="/socket.io/socket.io.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.js"></script>
<script>
//加载io
var socket = new io();
//表但按钮提交时
$('form').submit(function(e){
//取消原来提交功能
e.preventDefault();
//向socket发送chat msg为key的信息
socket.emit("chat msg",$('.msg').val());
//发送之后将发送内容添加在信息框中
$('#showMsg').append($('<li>').text($('.msg').val()));
//消息框清空
$('.msg').val("");
return false;
});
//收到消息时
socket.on("chat msg",function(msg){
console.log(msg);
//在聊天窗口添加内容
$('#showMsg').append($('<li>').text(msg));
});
</script>
</html>
//Similar Posts类似的文章
套接字socket
node_modules
sun / neutron star / black hole / node modules
太阳/ 中子星/ 黑洞/ 节点模块
节点模块
1.安装npm包–rimraf
npm install rimraf -g
2.在cmd指令下,进入所需删除的node_modules文件夹的位置,再输入指令
rimraf node_modules
3.简单粗暴得秒删完成
其实也不用每次都安装node_modules,直接使用软连接即可:
windows 使用mklink /j node_modules %APPDATA%\Roaming\npm\node_modules
linux使用ls -s node_modules %APPDATA%\Roaming\npm\node_modules
nodejs中package.json中的依赖必须每个项目都有自己的node_modules文件夹,而无法在多个项目之间共用一套node_modules(像Java中的Maven那样)。
依赖管理是每个现代语言的标配。依赖管理和打包工具是两个概念,npm是依赖管理,webpack是打包工具。 在Java中,maven既能实现依赖管理又能实现打包。
何为依赖管理?
依赖管理说白了就是构建一个有向无环图。项目A依赖项目B,项目B依赖项目C,那么当你的项目依赖A的时候,依赖管理工具会自动让你的项目依赖B和C。 要想构建有向无环图,最关键的是要将项目转化为有向无环图中的结点。所以对于项目往往有description,作者信息,版本信息等额外信息。 依赖管理最难解决的问题就是版本问题。库A依赖库B,库C也依赖库B,但是库A跟库C所依赖的库B不是同一版本,如果库B的这两个版本兼容还好,如果不兼容就坑大发了,这是无解的问题。
Java,Python,Node依赖管理。
- Java中的Maven仓库在开发者电脑上是全局的,所有项目的依赖都集中存放在本地仓库中。每个项目都有pom.xml指明依赖本地仓库中的哪些库。
- Python中的pip跟maven很像,在开发者电脑上也是集中存放包,但是它不存在版本问题。也就是说,在你的电脑上每个python库都只有一个版本。既然如此,当你依赖某个库的时候,就无需指明版本号,直接引用包的名称就可以了。
- Node中的依赖如果你不写package.json,那么依赖的就是全局的库;如果写了package.json,就会把所有依赖下载到node_modules文件夹。
node_modules文件夹有利有弊
最明显的坏处是:
- 每次都需要安装依赖,费流量,网速慢时很费时间
- 浪费磁盘空间,每个node_modules中包含的工具很多,动辄20M
最明显的好处是:
- 使用package.json安装好之后,node_modules文件夹中没有版本信息,从而package.json可以删掉了。 移动/复制/打包项目比较简单,对于开发、部署都有好处
- 对于设计npm的人来说,这是最省事的包依赖方法。这就好比maven安装依赖之后自动将jar包安装到项目的lib里面。
- 随意改代码。安装在node_modules里面的东西,你可以随便改,无需担心对其它项目的影响。在Java中使用maven管理项目时,如果想要定制某个库,就需要更改这个库的源代码,这时就需要把这个库的源代码复制到项目中,跟node_modules是一个道理。npm的设计者大概认为:前端都是经常修改库的源代码的。
我认为不同语言对于依赖的定位不同。
Java中的库是严谨的库,Python中的库是玩具一样、随手写就的库,Node里的库是代码片段一样的库。
Node里面的库既然定位就是代码片段,那么当然要将代码片段跟你的项目放在一起了,这样才方便你修改这些代码片段。可是随着时间推移,node中的库越来越大、越来越严谨,这种对待代码片段的方式就有些不好了。
node写服务器
解析http协议,发送一个网络请求,关注网络本身。
node主要是为了tcp/ip协议,长链接。
(百度百科)
1. 短连接: 指通讯双方有数据交互时,就建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。
2. 长连接( keep-alive ): 指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包,
通俗的讲:长连接就像 是一通不挂机的电话:双方一直在线,随时可以通话,当然前提是你有足够的话费
而短连接就相当于日常电话: 当你有事的时候就给对方打电话,没有事情则及时挂断,下次有事情则重新打电话;
二. 实现过程:
1. 首先短连接的过程: 连接->传输数据->关闭连接
具体就是 浏览器
client发起并建立TCP连接 -> client发送HttpRequest报文 ->
server接收到报文->server handle并发送HttpResponse报文给前端,
发送完毕之后立即调用socket.close方法->client接收response报文->
client最终会收到server端断开TCP连接的信号->client 端断开TCP连接,
具体就是调用close方法。
也可以这样说:短连接是指SOCKET连接后,发送接收完数据后马上断开连接。
因为连接后接收了数据就断开了,所以每次数据接受处理不会有联系。
这也是短连接HTTP协议无状态的原因之一。
2. 长连接的请求过程是:
连接->传输数据->保持连接 -> 传输数据-> ··· ->直到一方关闭连接,
多是客户端关闭连接
长连接的缺点:
采用新的协议,后端需要单独实现
客户端并不是所有浏览器都支持
代理服务器会有不支持websocket的情况
无超时处理
更耗电及占用资源
三、用途:
1.短连接: web网站的http服务一般都是用短连接
2. 长连接:适用于点对点之间的频繁通讯,
如通信、股票、Feed、直播、共享桌面,特别适合于客户端与服务频繁交互的情况下,如实时共享、多人协作等平台。
他山之石
https://www.npmjs.com/ # npm官网
https://npm.taobao.org/ # 淘宝npm镜像
expressjs
官网:http://expressjs.com/
中文官网:http://www.expressjs.com.cn/
ejs
https://ejs.co/ #官网
https://www.npmjs.com/package/ejs #npm
http://www.jianshu.com/p/67dda091fc68