node介绍
官方:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境。
私人:一个“软件”,能运行js代码
说白了Node.js存在意义就是让JavaScript可以和其他的后端语言一样能够在服务器上运行,它的存在对于JavaScript有了质的飞跃,可以让前端语言JavaScript在写完之后交给Node.js进行编译和解释
现在已经有很多公司在使用node作为服务器语言了,国外的:linkedin/paypal/twitter 国内的: 知乎的主站推送、网易(部分后台)、阿里(部分后台)
另外对于中小型项目的后台快速开发非常适合。
node和其他常用软件的区别
-
图标 -》 node没有图标
-
顺序:其他软件可以先打开软件,然后在选择文件
node不可以,在运行的时候,你就要告诉他运行哪一个文件
-
可视化:其他软件有一个可视化区域 比如chrom的F12
node只能在命令行里运行
-
解析引擎: js的解析引擎只能解析js的语法
-
前端js三大核心:
- DOM文档对象模型:因为你的js是被引入在html文件里面,所以我们可以操作DOM,如果不引入到html文件则不能操作DOM
- BOM浏览器对象模型,因为你的js文件是被运行在浏览器里面的,需要用到浏览器的内核中的解析引擎来解析js文件执行,所以我们js可以操作浏览器
- ECMAScript 语法:js的语法标准,前端js是被局限在浏览器里的,只能操作最外层的window,不能操作电脑中的文件
-
nodes :核心
- ECMAScript 语法:js的语法标准
- I/O : 输入输出流,读写磁盘
- system : 操作操作系统
-
书写代码:还是以.js为后缀的文件,写的还是js的语法
如果你将来要把这个js文件引入在html页面里放在前端用,你就不能写I/O 的API,不能写操作操作系用的API
如果你将来要把这个js文件在node上运行,你就不能写操作DOM和BOM的操作,但可以写I/O 和操作操作系用的API
-
node的使用:
-
下载:直接在官网下载,下来稳定版本
-
安装:双击下载下来的安装包,一路下一步,傻瓜式安装
注意:一个电脑上只能安装一个版本的node,安装第二个版本会把第一版本顶掉,不能回滚
安装完毕没有图标
-
检测安装:
-
打开命令行cmd (MAC 终端)
-
输入指令 $ node --version $符号不是指令,它代表后面的是指令
-
出现版本号就表示安装成功 10.4以上都可以用
-
-
卸载
- 打开我的电脑
- 控制面板 卸载程序
- 右键卸载
-
-
环境变量
对于你的电脑的操作系统环境,开发的一些变量(一个一个的文件夹)
运行软件,就是找到你安装包里的 名字.exe 文件
环境变量,就是可以你设定开发哪些变量(目录)给你的命令行,只要你把指定目录放在环境变量里面配置好,当你书写一个 名字 的时候,它就会去你这些目录里面找有没有名字对应的 .exe文件,如果有,就给直接执行,如果没有就报不是内部或外部命令
-
利用环境变量里的内容运行文件,
例子: chrome 你电脑上的文件路径
node不光可以运行绝对路径,还可以运行相对路径的文件,其他软件只能使用绝对路径
在cmd中--
chrome c:\User\m1330\desktop\index.html 使用chrome 这个软件运行后面绝对路径指定的文件
node c:\User\m1330\desktop\index.js 使用node运行后面绝对路径指定的文件
在桌面点击鼠标右键,选择 在此处打开cmd窗口
node \index.js 使用node运行后面相对路径指定的文件
-
总结:node(2009年才有的node)
利用node 一个工具,在命令行里执行我的js代码,在执行过程中没有了DOM和BOM,但是可以操作电脑了,之前js文件运行在浏览器里,被限制了,最大只能操作到window
实际意义:
可以电脑,就可以操作数据库(数据库也是我们电脑上的一个软件),创建服务被当作服务器使用,可以靠js语言来进行服务端开发,js写后端
目前市场上的开发没有js做服务器的,因为js有致命的缺陷,他是一个脚本语言,js弱类型,服务器的语言必须要严谨,还有就是安全防护,js做的不是很好
命令行常用操作
-
目录结构
打开cmd窗口,在前面会有一个目录出现,表示你所处的目录
-
切换目录
- cd … =》 回到上一级目录
- cd 当前目录下的指定文件夹 =〉 进入指定目录
- dir =》查看当前目录结构 ls (mac)
- 盘符: =〉切换盘符
- tree: =》树状j结构查看当前目录的所有后代目录结构
-
其他指令
- cls (Mac 上是 clear)=》清屏
- systeminfo: =〉系统基本信息
- ping 地址 =》查看链接信息。 如果访问百度网站后可以获取链接的状态信息
- ipconfig (mac: ifconfig) =>查看网关信息
-
vscode自带一个命令行
可以使用快捷键 ctrl+` (ctrl+反引号)打开
但是其和cmd不一样,选择右侧的小框的下拉菜单选择show后在上侧选择Command Prompt即可切换为cmd模式
vscode的命令行工具右侧的X号是最小化,垃圾桶才是退出关闭
开始程序
命令行
node运行js文件:直接在命令行书写js代码,先打开命令行在命令行输入指令node 按enter键确定
进入输入状态,前面没有目录,可以看到下面面的提示 " >_ " 表示已经进入node的运行状态了,就可以输入js代码了(和以前网页中写js基本一致,等价于浏览器的控制台),只能输入js代码,其他代码会报错
这里我们输入console.log( “helloWorld” ) 确定 即可看到打印的helloWorld
在node中,每一行代码执行完,都会显示一下返回值,这里没有返回值,所以有个undefined。
-
缺点:没有代码提示,存不下来,只要一关闭cmd就没有了
-
结束Node的运行状态:连续按两次 ctrl+c 就可以退出,执行状态如下图,
在想要结束node的时候,不要不退出直接关闭cmd,多次会对电脑造成伤害
在命令行运行js文件:
- 把js代码写在一个js文件里,打开命令行,切换目录到你要执行的js文件所在目录 书写执行 node js文件名称 回车
即:node 绝对路
回车,相当于以node这个软件在执行你指定的js文件。 (不需要先进入node运行时环境)
- 建议创建一个用于放js的文件夹,取名nodeTest
然后在里面创建一个js文件, 取名 main.js,(注意文件的编码格式最好是utf-8,不然输出中文会乱码。)
console.log("helloWorld"); //输出一行文字
console.log(__dirname); //输出文件夹名字
console.log(__filename); //输出文件名
写好的js文件如何使用node 运行呢?
前面我们的命令行是从开始菜单打开的,node的执行是在用户管理员 文件夹下,
如果我们要在某个自定义的文件夹执行node,那么我们可以在 自己的文件夹 按住shift并点击右键,菜单中点击“在此处打开命令行”,即可定位到该目录.
此时直接输入 node 需要执行的文件名 确定 就执行了,可以看到打印的三行数据 helloWorld以及文件夹名和文件名
node开发模式
如何书写代码,必须模块化开发,node的模块化标准叫做Common JS,其实就是导入导出文件的语法
node为什么必须模块化
-
前端
当js代码引入html页面的使用的时候,一个页面引入多个js文件,这些js文件之间会有一个共同的window,将想在别的文件里面使用的内容挂载在window上
-
node
js代码在命令行里面运行,每一次只能执行一个js文件 语法为:node 文件名 回车,多个js文件之间没有关联,也没有共同的顶级对象出现,只能使用模块化的方式,导入导出
-
什么是模块
一个独立的js文件就是一个模块,里面存储一类方法,每一个js文件都不能访问其他文件里面的变量,当你在node环境下的时候,每一个js文件我们叫做一个模块作用域,就是一个独立的作用域,我们不能访问别人,别人也不能访问我的
-
node里的模块分类
-
自定义模块
我们自己书写的js文件
-
内置模块
就是你安装node的时候自带的一些模块,node原生的模块
(比如操作文件的,操作操作系统)
-
第三方模块
其他人写的一些js文件,里面封装好一些功能,你需要的时候下载下来再使用
-
node怎么实现模块化
每一js文件都天生自带一个变量叫做module,是Node自带的模块儿化对象,可以在node运行运行的js文件里,打印module看一下
该对象下默认有一个空对象 – exports ,我们将需要暴露的对象直接赋值给exports就向外暴露了接口,
console.log(module);
/*
Module {
id: '.',
//--------当前文件的路径
path: '/Users/wei/Desktop/lianxi ',
//-----我向外暴露的内容,我允许别人使用的我这个文件里的哪些内容
exports: {},
//---------哪些文件在引用我
parent: null,
//-----当前这个文件的文件名称(带有绝对路径)
filename: '/Users/wei/Desktop/lianxi /a.js',
//-----我有没有被导入过
loaded: false,
//----我引用了哪些其他的文件
children: [],
//----你下载的第三方模块(node_modules指代)的存储路径
paths: [
'/Users/wei/Desktop/lianxi /node_modules',
'/Users/wei/Desktop/node_modules',
'/Users/wei/node_modules',
'/Users/node_modules',
'/node_modules'
]
}
*/
1、编写模块并暴露接口
在module01.js中写入下面的代码,编写了两个方法,并使用node自带的 module对象向外暴露接口。
导出语法:
1:module.exports 向这个对象里添加成员就可以了
需要暴露的方法以属性 附加到 module.exports对象上
2:也可以直接把module.exports 替换掉
function fn01(){ //编写一个方法 fn01
console.log("module01-fn01");
}
function fn02(){ //编写一个方法 fn02
console.log("module01-fn02");
}
//-----暴露接口1
//-----将fn01向外开放,别人引入该模块后,就可以使用fn01了
module.exports.fn01=fn01;
//-----暴露接口2
module.exports = {
"fn01":fn01,
"fn02":fn02
}
2、使用接口
解释:(1)使用node自带方法 require引入模块,格式 require(“文件路径”),【这里的 ./ 表示当前路径 】,
作用:导入该文件(路径如果后缀为.js 可以省略不写)
返回值:就是指定文件内部的module.exports
在test.js中写入 如下代码
var module01 = require("./module01.js"); //使用 require引入模块,并申明一个变量来接收接口暴露的对象
module01.fn01(); //访问接口暴露的方法
我们申明了一个变量 module01来接收引入模块暴露的对象,这个对象就是我们上一步中给exports赋值的那个对象,关系如下:
module01 == {
"fn01":fn01,
"fn02":fn02
}
既然我们使用 module01接收到了这个对象,我们就可以调用 里面的方法或属性了。 使用 module01.fn01(),就可以执行 module01.js 中的 fn01方法。
3、运行看效果
使用node 运行test.js的时候,就可以看到fn01方法执行 打印的 “module01-fn01”;
4 导入时可以按需导入
-----------------------------a.js
const a=120;
const b=23;
module.exports={
a,
b,
a_A(){
console.log('我是a文件里的a_A函数')
},
a_B(){
console.log('我是a文件里的a_B函数')
},
}
--------------------------b.js
var newValue = require('./a');
const {a_A}=require('./a'); //-----以结构赋值的方式按需导入想要的数据
console.log('------b1b1b1',newValue.a)
console.log('------b2b2b2',a_A)
多个模块的引用
我们上一步只引入了module01.js ,这一步我们引入多个模块
1、我们先将 module02.js 也写成接口,代码和module.js基本一致,只是 打印的 内容 改为了 “module02-fn01”
function fn01(){ //编写一个方法 fn01
console.log("module02-fn01");
}
function fn02(){ //编写一个方法 fn02
console.log("module02-fn02");
}
//暴露接口
module.exports = {
"fn01":fn01,
"fn02":fn02
}
2、我们在 test.js中引入模块
将原来的test.js代码修改成如下代码:
var module01 = require("./module01.js"); //module01.js 的暴露对象 使用 变量 module01接收
var module02 = require("./module02.js"); //module02.js 的暴露对象 使用 变量 module02接收
module01.fn01(); //访问module01.js的方法
module02.fn02(); //访问module02.js的方法
3、执行test.js
即可看到两个模块的方法都被调用了,就算模块中使用了同样的方法,在使用的时候,由于定义了不同的变量来接收,也不会互相影响,而且我们要使用哪个模块就直接引用就可以,这就是模块儿化的优势。
fs文件操作模块
读取文件
异步读取
node自带文件操作模块 fs,里面存放着操作文件相关的方法,使用时只需要直接引入即可
首先准备一个需要被访问的文件a.txt文件
编辑 a.txt。任意写入点文字 (注意 编码格式 修改为 utf-8,如果是默认格式,中文文字读取的时候会是乱码)
编辑 我们的js文件,fs01.js,写入一下代码
-
第一行代码我们直接引入了 node的自带模块 fs (文件操作模块)
-
调用了 fs模块下的读取文件方法 readFile()异步读取文件
语法:fs.readFile(路径[,格式],回调函数)
内部传入的三个参数分别为
1、读取的文件路径
2、编码格式 读取文件默认是buffer格式的文件,可以选填‘utf-8’ 表示读取的是utf8格式的内容
3、回调函数
读取成功后执行的回调函数,回调函数中有两个参数 err 和 data :
err表示错误信息,当文件读取错误的时候会赋值错误提示
data表示读取成功时的文件内容。
var fs = require("fs"); //导入node 内置模块 fs:文件系统操作模块
console.log(fs);//--查看一下所包含的方法
consoe.log('start');//---查看异步读取
fs.readFile("./a.txt","utf-8",function(err,data){ //读取文件
if(err)return console.log('文件读取出错',err);
console.log('文件读取ok',data);
});
consoe.log('end')
一般情况下,如果读取出错我们需要抛出异常终止程序。所以需要将代码修改如下
var fs = require("fs"); //node 内置模块可直接引入 fs:文件系统操作模块
fs.readFile("./a.txt","utf-8",function(err,data){ //读取文件
if(err) throw err;
console.log(err,data);
//读取的后续操作可写在这里
});
同步读取文件
格式:fs.readFileSync(路径[,格式]) 同步读取没有回调函数,不会吸收错误
作用:同步读取文件
返回值:就是读取到的内容,如果出错就终断执行了,直接控制台报错
// 读取文件
const fs=require('fs');
console.log('start');
var res=fs.readFileSync('文件.text','utf-8')
console.log('同步读取文件成功后的返回值',res)
console.log('end');
//----
//start
//同步读取文件成功后的返回值 你好啊布布妈妈,你要加油哦~
//end
读取图片的方式
// binary 翻译为 二进制 ,以二进制格式读取图片不能uft8
fs.readFile(图片路径,'binary',(err,data)=>{
if(err){
//错误时设置响应状态码为404
res.setHeader('Content-Type','text/plain;charset=utf8')
res.writeHead(404)
return res.end('读取出错');
}
res.setHeader('Content-Type','image/*;charset=utf8')
res.writeHead(200)
return res.end(data,'binary')
})
写入文件
异步写入文件
格式:fs.writeFile(路径,内容,回调函数)
路径:如果路径存在直接写入内容,如果路径不存在,node会自动创建一个文件再写入
内容:你要写入文件中的内容
回调函数:必须写,如果没有在要干的事也要写一个空的函数,不写报错
只有一个参数,当出现写入错误的时候,会有错误提示 err
注意:完全覆盖式的写入
writeFile是将内容替换掉,如果想要追加写入,可以使用appendFile方法
案例:
// 读取文件
const fs=require('fs'); //----引入模块
console.log('start');
fs.writeFile('文件2.text','加油牛牛,不怕困难',(err)=>{
console.log('err文件写入失败',err)
console.log('文件写入成功了')
})
console.log('end');
//---打印
/*
start
end
err文件写入失败 null
文件写入成功了
*/
将上一步读取的数据写入b.txt文件中
将 fs01.js代码修改如下(中间添加写入文件的代码):
var fs = require("fs"); //node 内置模块可直接引入 fs:文件系统操作模块
fs.readFile("./a.txt","utf-8",function(err,data){ //读取文件
if(err) throw err;
//读取的后续操作可写在这里
fs.writeFile("./b.txt",data,function(err){ //写入文件
if(err) throw err;
})
});
同步写入文件
格式:fs.writeFileSync(路径,内容);
没有返回值,什么都没有,写入即可
删除文件
使用 unlink方法异步删除文件
格式:fs.unlink(路径,(err)=>{})
unlink方法有两个参数,1、要删除的路径,2、回调函数,如果出现错误,会有err提示
var fs = require("fs"); //node 内置模块可直接引入 fs:文件系统操作模块
fs.unlink("./b.txt",function(err){ //删除文件
if(err) throw err;
})
创建文件夹
使用 mkdir 方法 ,
格式:fs.mkdir(“文件夹名称”,function(err){})
两个参数 1、文件夹名,2、回调函数,如果出现错误,会有err提示
返回值:没有返回值
var fs = require("fs"); //node 内置模块可直接引入 fs:文件系统操作模块
fs.mkdir("c",function(err){ //创建 c 文件
if(err) throw err;
})
修改文件名/文件夹名
格式:fs.rename(“文件夹名称或文件名称(带后缀)”,‘新的文件夹名称或文件名称(带后缀)’,function(err){})
参数 1、需要修改的文件夹名称,或文件名称带后缀(当前目录下)
2、新的文件夹名称,或文件名称带后缀
3、回调函数,如果出现错误,会有err提示
var fs = require("fs"); //node 内置模块可直接引入 fs:文件系统操作模块
fs.rename("c","d",function(err){ //将c 文件夹名字 改为 d
if(err) throw err;
})
路径模块
path模块包含一系列处理和转换文件路径的工具集,通过 require('path')
可用来访问这个模块。
为什么要使用路径模块
不同操作系统的路径分割符不同,window下是\ / linux 是/
path.extname(文件名)
获取路径中的扩展名,如果没有’.’,则返回空
var path=require('path')
// console.log(path)
console.log(path.extname('a.js')) // .js
path.isAbsolute(‘路径地址’)
作用:判断是不是绝对路径
返回值:一个布尔值。true 表示绝对路径。false 表示非绝对路径
-
路径表示方法
文件名 a.js 同级目录下的a.js文件 ./ ./a.js 同级目录下的a.js文件 ../ ../a.js 上级目录下的a.js文件 / /a.js 根目录下的a.js文件
相对路径:和当前位置有关系的路经,叫做相对路径
绝对路径:从根目录出发的路径叫做绝对路径
var path=require('path')
console.log( path.isAbsolute('/文件.text')); //---true
console.log( path.isAbsolute('./文件.text')); //---false
path.join()
作用:多个参数拼接成相对路径
path.join
方法用于连接路径。该方法的主要用途在于,会正确使用当前系统的路径分隔符
,Unix系统是”/“
,Windows系统是”\“
。
格式:path.join(‘地址1’,‘地址2’,‘地址3’,…)
反回值:拼接好的相对路径
注意:按照第一个地址开始的路径进行拼接,没有根目录这回事,任何个一个地址,都是拼接在前一个地址的后面
除非是…/ 会向前跳一级
// 导入path模块
const path = require('path');
// 路径拼接
let finialPath = path.join('itcast', 'a', 'b', 'c.css');
// 输出结果 itcast\a\b\c.css
console.log(finialPath);
---
console.log('***',path.join('./y.js','/a','./b.js','./7'));
//y.js/a/b.js/7
console.log('***',path.join('./y.js','/a','./b.js','../7'));
//y.js/a/7
path.resolve()
作用:多个参数拼接成绝对路径
格式:path.resolve(‘地址1’,‘地址2’,‘地址3’,…)
返回值:拼接好的绝对路径
注意:每一个参数位置都是相对于当前文件所在的目录设置的;如果你写/XXX 回直接回到根目录
console.log(path.resolve('./a','b'));
// /Users/wei/Desktop/lianxi /a/b
console.log(path.resolve('./a','b','../c'));
// /Users/wei/Desktop/lianxi /a/c
console.log(path.resolve('./a','b','../c','/f'));
//
path.parse()
作用:解析一个路径,成为一个对象
语法:path.parse()
返回值:一个对象,里面包含了一个地址的所有信息
console.log('$$$$',path.parse('/Users/wei/Desktop/lianxi /a/c.js'));
//---
{
//--根目录
root: '/',
//-- 文件所在的文件夹的绝对路径
dir: '/Users/wei/Desktop/lianxi /a',
//-- 完整文件名称
base: 'c.js',
//-- 后缀名
ext: '.js',
//-- 文件名称
name: 'c'
}
__dirname
获取当前文件所在的绝对路径
console.log(__dirname); 直接获取不需要path.
使用相对路径还是绝对路径
大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录
在读取文件或者设置文件路径时都会选择绝对路径
const fs = require('fs');
const path = require('path');
console.log(__dirname);
console.log(path.join(__dirname, '01.helloworld.js'))
fs.readFile(path.join(__dirname, '01.helloworld.js'), 'utf8', (err, doc) => {
console.log(err)
console.log(doc)
});
url模块
内置操作url地址的模块,直接引入就可以使用
格式:url.parse(‘url地址’[,是否解析query])
第一个参数是:要解析的url地址
第二个参数是:默认值是false,不解析query,true,会自动解析query
返回值:返回一个对象,包含整个url地址的所有信息
var url=require('url');
console.log(url)
var res=url.parse('https://www.baidu.com:8080/a/b/c?a=100&b=200&c=300#123',true)
console.log(res); //---第二个参数为true时,解析后的query,可以直接获取解析后的值
console.log('*****',res.query.a);
/*
{
//传输协议
protocol: 'https:',
slashes: true,
auth: null,
// 域名带端口
host: 'www.baidu.com:8080',
//端口
port: '8080',
// 域名
hostname: 'www.baidu.com',
//哈希
hash: '#123',
// 查询字符串
search: '?a=100&b=200&c=300',
//查询字符串里面的参数信息
query: 'a=100&b=200&c=300', //-----第二个参数为false时
query: [Object: null prototype] { a: '100', b: '200', c: '300' }, //---第二个参数为true时,
/路径名称,不含请求参数的请求地址
pathname: '/a/b/c',
// 路径带查询字符串
path: '/a/b/c?a=100&b=200&c=300',
// 完整路径
href: 'https://www.baidu.com:8080/?a=100&b=200&c=300#123'
}
*/
网络基本概念
网站组成分为两大部分:客户端 和 服务器端
客户端:在浏览器运行的部分,就是用户看到并与之交互的界面程序,使用html css js构建
服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑
客户端和服务器端通过请求和响应获取数据及创建数据,请求可以理解为客户端向服务器端发送一个指令,告诉服务器我要获取数据,响应可以理解为服务器在接收到指令以后对客户端进行回应,将客户端请求的数据发给客户端,网站实际就是基于请求和响应的应用结构
什么是服务器
早些年,电脑都是独立的,A电脑的文件就A电脑的,B电脑的文件就是B电脑的,但是如果A电脑想要给B电脑传输数据,那就需要拉一根线。
然后假如这时候,加入了一个新的C电脑,这时候三台电脑要互相连接的话,需要接3根线
但是如果有4台电脑的话,就需要每个电脑都接3根线,也就是需要接6根线
那要是随着时间的推移,电脑数量越来越多,那需要的线材也越来越多,需要的接口也越来越多,这样的话就肯定不行的。这时候有人就想到了,让所有的电脑接到一台电脑上,然后让这台电脑转发一下数据不就行了么?那这台负责转发数据的电脑,就被我们成为服务器。
什么是IP地址
ip地址:是互联网设备的唯一标识
那这里就有问题了,A电脑要给C电脑发送数据的话,就必须要知道对方是哪一台机器,不然数据就有可能被服务器送给B或者D,那为了准确送达数据,我们就需要给电脑加上门牌号和地址,就好像你快递寄送的时候也需要地址一样,数据上也需要打上地址。
而我们说的IP地址,就是电脑里用于交互数据的地址,有了IP地址后,你的数据就不会被错误的发送到别人的电脑上,比如往192.168.1.123的电脑上发送一个表格,那这个数据就肯定会被送到192.168.1.123,而不是192.168.1.100上。
什么是公网IP什么是局域网IP
你要寄送快递的时候,需要填省,市,县,然后是小区,门牌号。
其中局域网IP就类似我们的小区门牌号,如果你在小区里说我家是12号楼1单元101,大家都知道你是这个地方,但是你要去别的小区说你是12号楼1单元101,他们肯定不知道你说的是你的小区,而会认为是他们的小区。所以局域网IP就是用于某一群体内部交互数据的,他的地址只在这个群体内部生效,拿出这个群体就失效了。
而如果是小区与小区之间交互数据的话,就必须要前缀小区的名字,先送到小区,再分给门牌号。这里举个例子吧,寄送到月亮湾小区的12号楼1单元101,那么需要先送到月亮湾,然后再分给12号楼1单元101。
小区的名字是唯一的,全中国就这一个小区叫月亮湾,所以你说月亮湾,谁都能通过查询知道月亮湾在哪里,是哪个小区。那么这种你一说,大家都能知道位置和是什么的地址名称,就叫公网IP。
也就是说公网IP相当于是可以直接访问的IP地址,相当于省市县这种全国唯一的地址,而局域网IP是在公网IP之下的门牌号。
A公司中的a电脑如果给自己公司的b电脑传输数据,他只需要打上192.168.1.2直接就能送过去,因为他们都在一个公网IP下,所以他们的门牌号是认识的,只需要局域网IP就可以寄送数据,但是如果A公司的a电脑要给B公司的c电脑送数据,就需要先将数据送给本公司公网IP下的117.48.202.168,然后交给B公司公网IP下的202.108.22.5,然后再交给192.168.1.3,直接发送给192.168.1.3的话就会发送到自家公司的c电脑上而不是B公司的c电脑了。
网页浏览的原理
大家日常上网的时候,打开的网页本质上就是你从对方服务器上获取的文件,比如说你现在正在看的知乎上的文章,就存储在知乎的服务器上,你通过知乎客户端的浏览器,获取到了这些数据,并下载到了你的手机缓存当中,然后你的手机再把他们显示到屏幕上。
浏览网页的本质,就是下载文件,并将下载下来的网页文件变成你所能看到的图像。网页文件一般是.html结尾的,不相信的话你可以用电脑浏览器,随便打开一个网址,右键空白处,网页另存为,然后你就会发现你存储下来了一个.html结尾的网页文件,这时候你就算断网,双击这个网页文件你依旧可以用浏览器浏览,因为这个html文件被你保存到电脑上了。
所以浏览网页的原理就是,在互联网上找到了对方的电脑,然后从对方的电脑里拷贝出来html网页文件到你的电脑上,并将其转化成了文字和图片显示到显示器或手机上。
域名与IP的关系
既然你要浏览网页需要在整个互联网上找到对方的电脑,那你就需要输入对方的IP才可以访问,比如大家可以在浏览器里输入这个IP地址【14.215.177.39】你们可以看看打开的是不是百度的首页。是不是很有意思,你靠着IP地址可以访问百度,你输入http://www.baidu.com也可以上百度,这是什么原理呢?
在早期的时候,上网就是这么麻烦,你想要访问对方的网站,你必须要知道对方的IP,然后在你的浏览器里输入IP地址,然后就可以访问了,但是IP地址是4组数字,记IP地址的难度不亚于背一个陌生人的手机号,于是乎,我们用一串英文字母来代替IP地址,这就是网站域名,比如百度的网站域名就是http://www.baidu.com,而百度的IP地址就是14.215.177.39,网站域名很好记,而且域名也可以自定义。
但是这里就有一个问题了,你输入的是域名,你的电脑该怎么将他变成IP地址呢?就比如你输入的是http://www.baidu.com,为什么你的电脑知道对方的IP是14.215.177.39呢?这个东西就是hosts文件,hosts文件就在你的C:\windows\system32\drivers\etc文件夹下,他相当于电脑的电话本,他记录着每一个域名对应的IP地址,当你输入域名而不是IP的时候,他就会在这个电话本里找到对应的域名,然后把他转化成IP地址。
DNS解析服务器
但是这样也有问题,那就是Hosts文件是有限的,就和你不可能拥有这个世界上所有人的电话号码一样。既然我们自己不可能拥有全世界所有人的电话号码,但是我们可以将收集电话号码这个任务交给一个专门来干这个活的人,然后大家想要问电话的时候去他那查一下就可以了。
这就是DNS服务器,DNS服务器有着相当全的域名和IP,当你输入一串网站的时候,这串网站并不会直接访问,而是先将这个网站发送给DNS服务器,DNS服务器帮你把这串网站变成了IP地址,然后返回给你的电脑,你再访问这个IP地址,这样就解决了IP难记,而域名不能直接访问的问题了。
所以这样就可以解释文章开头那个故障了,你打不开网页,却可以上QQ,因为上QQ不需要涉及到DNS解析服务,直接访问的就是腾讯服务器的IP,但是你打开网页输入的是域名,而你的DNS服务器输入错误或者DNS服务器炸了,所以你电脑不知道这串域名对应的IP,你自然就没法访问这个网站,而如果你手动直接输入IP,你依旧是可以访问网站的。
什么是DNS劫持?
那DNS服务器会告诉你A网站的IP是A,B网站的IP是B,那假如你输入的A网站域名,但是他给你B的IP,你是不是就访问到别的网站去了?
既然DNS服务器可以这么玩,我是不是可以将用户引导到我的网站上?比如你要看个电影,然后你输入了爱奇艺的官网,然后我作为DNS服务器,我把优酷的IP返回给你,最后就是你虽然输入的爱奇艺官网,但是却得到了优酷的IP并访问了优酷的IP,进入了优酷的网站上。
当然这里我只是举个例子,我举这个例子就是想告诉大家,DNS服务器想给你返回什么IP就给你返回什么,所以他可以在幕后操作一些东西。最简单的就是广告。比如A网站没有广告,你直接访问就是没有的,但是你的DNS服务器把A网站下载下来了,给这个网站加了个广告,然后重新上传到了一个IP上,并把这个IP告诉了你,那就是你虽然输入的A网站的域名,但是你访问的是一个包含了广告的复制版A网站,虽然两者功能一样,但是却完全不是一个服务器上的。
这些就被我们成为DNS劫持,DNS劫持对于网络访问的影响和体验是非常严重的,除了DNS服务器,你的路由器同样可以这么干,所以在买路由器的时候也有人会关注这个路由器是否会进行DNS劫持。
端口
服务器不光可以提供网站服务,还可以提供邮件服务,文件的上传下载服务,当有请求来的时候怎么区分请求的什么服务,为了区分不同的服务
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。
URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。
组成:传输协议://服务器IP或域名:端口/资源所在位置标识
http://www.itcast.cn/news/20181018/09152238514.html
http:超文本传输协议,提供了一种发布和接收html页面的方法 html
通过url的地址或域名地址先找到要请求的服务器,请求是浏览器默认给我们加上80端口
资源在服务器中的位置,服务器可以拿到请求资源的信息,具体返回什么是由服务器说的算的
node网站服务器端
能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求
,能够对请求做出响应
。
首先,它得是一台电脑,在这个电脑中要安装node运行环境,然后在node环境中使用node.js创建一个能够接受请求和响应请求的对象,满足以上条件,它就是一个node服务器
客户端给服务端发送数据,叫请求,服务端接受到请求后返回相应的数据,叫响应。
开发过程中
在开发阶段,不需要买一台电脑,或者将代码部署到远程服务器,客户端和服务器端可以使用同一台电脑。
在开发阶段开发人员的电脑即可以充当客户端又可以充当服务器,因为我们即安装了浏览器又按装了node开发环境
既然是同一台电脑,如何通过网络的方式去访问
每台电脑都有自己一个独特的域名和IP
本机域名:localhost
本地IP :127.0.0.1
当在浏览器输入localhost就代表通过网络的方式去找到这台服务器
http模块,创建后台服务器
- 导入模块
node自带创建服务的模块 http,使用时只需要直接引入即可
const http=require(‘http’);
- 创建服务
格式:http.createServer(回调函数)
作用:创建一个http服务,创建web服务器对象
返回值:一个http服务,网站服务器对象
nodejs和javascript一样,都是基于事件驱动的语言,当什么时候去做什么事情
给服务器对象添加请求事件,使用on方法添加事件,第一个参数是事件名称即request,第二个参数就是事件处理函数
当请求来的时候就会去执行事件处理函数,事件处理函数的两个参数,req代表请求对象,对象中存储了和请求相关的一些信息,比如说请求地址,请求的id等,res代表响应对象,我们要使用这个对象里的方法对客户发来的请求作出响应,比如使用res中的end方法结束请求,并对请求作出响应
res.end(响应的内容)
当客户端发送请求的时候
var http=require('http');//引入http模块
const server=http.createServer(); //创建web服务器,返回值是网站服务器对象
server.on('request',(req,res)=>{
//响应
res.end()
})
- 监听端口
光有web服务还是不够的还要监听端口,才能向外界提供服务
格式:服务.listen(端口号)
监听某一个端口使用的方法
当你监听端口完毕的时候,在cmd里面运行起来这段代码,你的cmd窗口就会因为这段代码变成一个服务器,此时当客户端请求 lcoalhost:端口 的时候,每一个请求都会触发一次createServer 的时候的涵数
注意:
监听端口 端口范围:0~65535,尽量不要使用1024以下的端口,因为一些电脑内置的软件,还有一些插件会占用1024以下, 还有一些特殊的端口号,也不要用,比如数据库的端口号mysqel 的端口号3306,webstorm自己的服务器的 端口号63342,vscode自己服务的端口号是5500
可以使用一些吉利数字一般没人用,比如6666,8888,8080
creatServer里的函数就相当于一个事件处理函数,当前段发起一个请求的时候就触发了
node服务器是一个空服务器,它什么都没有,它只能给你启动起一个服务来,你想看目录,就自己写,所有东西都自己写,它只是一个空目录
var http=require('http');
const server=http.createServer(function(){
console.log('我被执行了')
})
server.listen(8080,()=>{
console.log('server running at port 8080');
})
http.createServer时候传递的函数,是会在前端的每一次请求都触发一次,这个函数接收两个参数,一个叫request,一个叫response
request:表示本次请求的所有请求信息
服务器解析完请求报文以后,组装的内容,你需要获取请求报文的什么内容,直接在request里面找
responese:将来会组装成响应报文的东西,你需要向响应报文里添加什么,就往这个response里指定位置添加
var http=require('http');
const server=http.createServer(function(req,res){
// req.url 表示本次请求的path
console.log('有一个请求进来了',req.url)
// 响应内容包含中文的时候要设置一下响应头
res.setHeader('Content-Type','text/html;charset=utf8')
// res.end() 响应报文的响应体,就是返回给前端的内容
//前后端交互只能交互字符串
res.end('我是响应体 hello')
//实际是我们应该根据请求的不同,返回不同的东西
})
server.listen(8080,()=>{
console.log('server running at port 8080');
})
首先创建文件 http01.js ,然后添加如下代码
//引入 http 模块
var http = require("http");
//创建服务 里面可以传入一个回调函数。回调函数有两个参数,1、请求信息,2、相应信息
http.createServer(function(request,response){
//表示请求结束,将结果返回给浏览器
response.end("haha");
//需要添加端口监听 listen(自定义端口号,这里我们常规使用3000),
}).listen(3000);
console.log("服务器已启动!")
注意:启动服务后,再修改了js文件以后,不会直接更新到页面,需要命令行按 ctrl+c 结束运行, 再重新运行 该js 后 ,再刷新 浏览器,才能看到效果。
请求地址只是一个字符串的标识,看起来是一个路径,在浏览器请求地址和在服务器上文件的真是路径可以是不一样的
读取一个文件并传输给页面
var http = require("http");//引入http模块
var fs = require("fs"); //引入fs模块
http.createServer(function(request,response){
fs.readFile("./a.txt","utf-8",function(err,data){ //读取文件
if(err) throw err;
response.end(data);//表示请求结束,将结果返回给浏览器
});
}).listen(3000);
var http=require('http');
const server=http.createServer(function(req,res){
// req.url 表示本次请求的path
console.log('有一个请求进来了',req.url)
// 响应内容包含中文的时候要设置一下响应头
res.setHeader('Content-Type','text/html;charset=utf8')
// res.end() 响应报文的响应体,就是返回给前端的内容
//前后端交互只能交互字符串
//实际是我们应该根据请求的不同,返回不同的东西
fs.readFile('./加油2.text',(err,data)=>{
res.end(data)
})
})
server.listen(8080,()=>{
console.log('server running at port 8080');
})
案例:使用nodejs 部署一个服务器,能够根据请求返回指定的页面和指定的文件
步骤:
-
1:http模块创建服务
1-1:导入http模块
1-2:创建服务
1-3:监听端口
-
2 根据请求返回对应的内容
2-1 拿到req里面的url信息
req.url 表示本次请求的path
2-2 进行if条件判断
2-3 读取指定文件返回给前端
-
3 读取到的文件返回给前端
3-1 导入fs模块
3-2 使用fs.readFile 方法读取到我服务器上的那个index.html文件
3-3 把读取到的内容返回给前端
// 导入模块
const http=require('http');
const fs=require('fs')
// 创建服务
http.createServer(function(req,res){
//拿到req里面的url信息
const {url}=req;
console.log('*****',url)
// 进行if条件判断,约定好你要哪一个html文件,把名称写清楚
if(url.endsWith('.html')){
// 读取index。html文件
fs.readFile('./html'+url,(err,data)=>{
if(err) return res.end('<h1>404页面没有找到</h1>')
// html页面设置响应头
res.setHeader('Content-Type','text/html;charset=utf8')
res.end(data)
})
}else if(url.endsWith('.js')){
fs.readFile('./js'+url,(err,data)=>{
if(err)return console.log(err);
res.end(data)
})
}else if(url.endsWith('.css')){
fs.readFile("./css"+url,(err,data)=>{
if(err)return console.log(err);
res.end(data)
})
}else{
// html页面设置响应头
res.setHeader('Content-Type','text/html;charset=utf8')
res.end('<h1>404页面没有找到</h1>')
}
}).listen(8080)
文件目录
returnPage
css
index.css
login.css
html
index.html
login.html
js
index.js
login.js
案例node.js
var http=require('http');
const server=http.createServer(function(req,res){
// req.url 表示本次请求的path
console.log('有一个请求进来了',req.url)
console.log(req.headers['accpet']);// req.headers 获取到请求报文里信息
conosle.log(req.method);//获取客户端的请求方式
// 响应内容包含中文的时候要设置一下响应头
res.setHeader('Content-Type','text/html;charset=utf8')
// res.end() 响应报文的响应体,就是返回给前端的内容
//前后端交互只能交互字符串
//实际是我们应该根据请求的不同,返回不同的东西
fs.readFile('./加油2.text',(err,data)=>{
res.end(data)
})
})
server.listen(8080,()=>{
console.log('server running at port 8080');
})
const fs=require('fs');
const http=require('http');
const url=require('url');
const path=require('path')
const mime=require('mime');
const app=http.createServer();
app.on('request',(req,res)=>{
const method=req.method.toLowerCase();
console.log(method);//请求方式
if(method=='get'){
//get请求
// url模块的parse方法可以解析请求的url, 获取请求的资源路径
//获取用户的请求路径,不带参数
let pathName=url.parse(req.url).pathname;
//当用户没有设置访问资源时默认访问首页
pathName=pathName=='/'?'/index.html':pathName;
let realPath; //realPath变量:静态资源的服务器上的真实地址
if(pathName.endsWith('html')){
// __dirname 为当前文件的决定路径, 拼接字符串,拼接后为静态资源的服务器上的真实地址realPath变量
//使用path.join 进行多个参数的地址拼接 因为不同的操做系统使用的路径分隔符不一样/ \
realPath=path.join(__dirname,'returnPage','html'+pathName);
}else if(pathName.endsWith('css')){
realPath=path.join(__dirname,'returnPage','css'+pathName);
}else if(pathName.endsWith('js')){
realPath=path.join(__dirname,'returnPage','js'+pathName);
}else{
realPath=path.join(__dirname,'returnPage','imgs'+pathName);
}
console.log('*****',realPath)
fs.readFile(realPath,(err,data)=>{
if(err){
res.writeHead(404,{'content-type':`text/html;charset=utf8`})
res.end('文件没有找到')
return;
}
// mime.getType(realPath) 返回 获取文件的真实地址所对应的资源类型
res.writeHead(200,{'content-type':`${mime.getType(realPath)}`})
res.end(data)
})
}else{
//post 请求
}
})
app.listen(8080)
http介绍
什么是http协议
http 传输协议
+ 前后端交互的方式(类似于发电报)
+ 前端以什么样的形式发送数据给后端
+ 后端以什么样的形式返回数据给前端
常见协议类型以及特点
UDP
不保证顺序,不保证是否丢失数据,
UDP传输效率高 支持1对多,多对1
张老师是个超级有趣的年轻小伙子 => 年轻小伙子超级有趣的。
http协议基于TCP协议,面向连接
传输协议(每一次的传输都要经历4个步骤)
1. 必须经历四个步骤
1-1. 建立连接
1-2. 发送请求(前端给后端)
1-3. 返回响应(后端给前端)
1-4. 断开连接
2. 只能由前端发起
+ 不能由后端主动沟通前端
3 一次只能说一个事
对于这个事情你可以尽可能的描述的详细,但是一次链接只能说一个事情
在发送请求的时候可以带很长的参数来描述这个事,但是一次只能一个事,想说另一件事得重新再发一个请求
4. 前后端交互只能交互字符串(但是对字符串的格式没有要求,可以使用json字符串,buffer字符串)
+ 所有其他数据类型都不可以
+ 中文会转成 url 编码
正常的字符串,一个字节应该是8位的0101011,beffer字符串是用8位来描述每一位,相当于描述一个字节是用了64位,实际它是将一个字节拆的更细,这样再一个字节可以存储更多的东西,buferr是大数据通讯的格式
一个请求的四个步骤
1:建立连接
基于TCP/IP 协议的三次握手
三次握手是浏览器和服务器的之间的,
目的:为了保证通道的连接,确保前端和后端都可以正常的发送及接收信息
三次握手和四次挥手
目的: 为了保证通道的连接
你在火车上,吃着自热火锅唱着歌,旁边一男的一直盯着你,你当然很不爽啦,然后你就问他
“你瞅啥”,结果对方毫不示弱,回复你:“瞅你咋地”,这个时候真男人绝对不要怂,直接怼他:“再瞅一个你试试”,对面继续盯着你。好了,言至于此,任何语言都是多余的,两人肯定要来一份真刀真枪的真男人对决。所以说TCP必须经过三次握手,才能达到可靠的传输效率。比如,我发送一次,你瞅啥,你不说瞅你咋地,那我也没办法啊,怒气值不够啊,那我只能继续吃着火锅唱着歌。但是如果对面给你回复了,瞅你咋地,这个时候你不回应,你怂了,那这个事情也就过去了,你们也干不起来。只有当你回复了一句 再瞅你试试 这个时候,我觉得必须通过一个男上加男来结束。少一次,你的信息,你的怒气值达不到,你的信息传达不过去。多一次,显得多余,干就完了呗。
两次握手只能保证单向连接是畅通的。
Step1 A -> B : 你好,B。
Step2 A <- B : 收到。你好,A。
这样的两次握手过程, A 向 B 打招呼得到了回应,即 A 向 B 发送数据,B 是可以收到的。
但是 B 向 A 打招呼,A 还没有回应,B 没有收到 A 的反馈,无法确保 A 可以收到 B 发送的数据。
只有经过第三次握手,才能确保双向都可以接收到对方的发送的 数据。
Step3 A -> B : 收到,B。
这样 B 才能确定 A 也可以收到 B 发送给 A 的数据。
为了保证断开连接
1. 前端给后端发一个消息: "响应体收到, 我要准备断开连接了"
2. 后端给前端发一个消息: "好的, 我知道你收到响应体了"
3. 后端在其给前端发一个消息: "我已经准备断开连接了, 当我再次收到你的消息的时候, 我就断了, 不会再次回覆"
4. 前端收到后端的第一个消息
5. 前端收到后端的第二个消息: "好的, 我断开了, 别回了"
2:发送请求
前端发送请求给后端,必须以请求报文的形式发送。请求报文,一个特殊格式的字符串(由浏览器进行组装,我们只负责填充)
请求报文:
请求行
请求头
请求空行
请求体
报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。(其实就是对话说明及对话的内容)
请求头,请求体,响应头,响应体
2:发送请求
HTTP 请求报文由3部分组成(请求行+请求头+请求体)
+ 前端发送请求给后端, 必须以 请求报文 的形式发送
+ 一个特殊格式的字符串文件(由浏览器进行组装)
请求报文的组成
=> 请求行
-> GET / POST: 请求方式(未完待续) 类似于说话的语气
-> ./login.php: 请求地址 向什么位置发送请求
-> HTTP/1.1: 传输协议版本 目前用的基本都是1.1 ,1.0版本已经不用了
=> 请求头(请求头都是键值对的形式)
-> 对本次请求的描述信息
-> Host: 请求主机
-> Accept: 期望的数据类型
-> UserAgent: 请求终端 通过这个可以判断是移动端请求还是pc端请求,不同的客户端请求返回结果可能不一样
-> Content-type: 请求体的数据格式
—> Referer 来源,即表示这是请求是从哪个URL进来的,比如想在网上购物,但是不知道选择哪家电商平台,你就去问度娘,说哪家电商的东西便宜啊,然后一堆东西弹出在你面前,第一给就是某宝,当你从这里进入某宝的时候,这个请求报文的Referer就是[www.baidu.com]
-> User-Agent 作用:告诉服务器,客户端使用的操作系统、浏览器版本和名称
-> Cookie: (未完待续)
=> 请求空行
用来分隔请求体和请求头的,因为请求体也是键值对,所有要分隔一下
=> 请求体
-> 前端携带给后端的参数,有的有,有的没有
-> 它将一个页面表单中的组件值通过param1=val1&parma=2的键值对形式编码成一个格式化串,它承载多个请求参数的数据,不但报文头可以传递请求参数,URL也可以通过/chapter15/user.html? param1=value1¶m2=value2”的方式传递数值
----我们要做的事就是确定一下请求方式,请求地址,参数是什么,浏览器拿到内容后自动组装成一个请求报文发送给服务端----
- 接收响应:
3. 接收响应
+ 每一个响应是由服务端接收到前端的请求以后, 给出的结果
+ 必须以响应报文的形式发送个前端
+ 响应报文
1. 状态行
=> 200: 响应状态码(未完待续)
=> ok: 对响应状态码的简单描述
=> HTTP/1.1 传输协议版本
2. 响应头
=> 对本次响应的描述信息
有若干个属性,形式为key:val,服务端据此获取客户端信息
=> Date: 服务器时间(世界标准时间)
=> Server: 服务器信息
=> Content-Type: 响应体的数据格式
=> content-length:响应体长度(一个汉字占3个长度的位置)
...
3. 响应体
=> 后端给前端的数据,即我们要的数据。
响应状态码
+ 以一个数字表示本次请求的响应状态
=> 成功: 创建成功, 删除成功, ...
=> 失败: 客户端失败, 服务端失败, ...
这个数字是100~599,分为五类
100~199:表示链接继续
200~299:表示各种意义上的成功
300~399:表示重定向
400~499:表示客户端错误
500~599:表服务端错误
网上有详细的
常见状态码
+ 101 表示连接继续
+ 200 请求成功 服务端如果没有返回状态码,默认就是200
+ 302 临时重定向
=> 本次请求临时使用 服务器 来决定浏览器跳转的页面
百度用的很多,就是我们在百度上搜索一个网页,点击后先跳到百度的统计页面,再跳到我们所要去的页面,这样百度可以统计有多少用户是通过百度点击过去的,这样百度就可以向平台收费
+ 301 永久重定向
=> 终身只要访问这个地址, 就会给重新切换到新的地址
访问 www.360buy.com(这个是京东最初的域名) 就永久重定向到了京东
因为最初www.jd.com被人注册,京东花大价钱买的,历史记录在案的买的最贵的域名就是这个
从这京东购买以后,就有了一个新的职业叫域名投资
+ 304 缓存
=> 当你访问过一遍这个页面以后,浏览器会自动缓存, 当你在其访问同一个地址的时候, 不会像服务器发送请求了, 而是从缓存里面获取
在浏览器中打开调试工具后,在左上角的刷新按钮上,点鼠标右键,可以出现 清空缓存并硬性重新加载
+ 400 客户端请求有语法错误,比如说请求路径错了,或者请求参数不匹配
+ 403 访问权限不够 这是客户端错误
+ 404 访问地址不存在,请求资源没有找到
+ 500 通用服务端错误,一般和客户端没有关系(一般不会给准确的服务端的错误信息,因为可以估测出服端的性能,服务端信息都是要保密,所以一般都是给一个通用的服务端错误信息)
+ 501 服务器过载或者维护,就是玩游戏,每周更新的时候给501,过载超过服务器的承受
4:断开链接
4:断开链接
基于TCP/IP协议的四次挥手,是为了保证链接断开
第一次:前端给后端发送一个消息:响应体收到我要准备断开链接了
第二次:后端给前端发送一个消息:好的,我知道你收到响应体了
第三次:后端再次给前端发送一个消息:我已经准备断开链接了,当我再次收到你的消息的时候,我就断了,不会再次回复。这个是伴随第二次一块发过去的,两个包一起发
第四次:前端收到后端的第一个消息,什么也不做
前端收到后端的第二个消息,告诉后端:好的我断开了,别回了,当这个消息来到服务端以后,服务端就把门关上了,不再回复
下一次再发送请求,就要从头跑一边,先建立连接,发请求,接响应,断开链接
请求报文
//引入 http 模块
var http = require("http");
//创建服务 里面可以传入一个回调函数。回调函数有两个参数,1、请求信息,2、响应信息
http.createServer(function(request,response){
req.headers // 获取请求报文
req.url // 获取请求地址 根据不同的地址,访问不同的页面
req.method // 获取请求方法
//需要添加端口监听 listen(自定义端口号,这里我们常规使用3000),
}).listen(3000);
console.log("服务器已启动!")
响应报文
//----------- 设置响应报文
// 在服务器端给客户端设置一些不同的状态码 默认值为200
res.writeHead(404);
res.writeHead(200, {'Content-type':'text/plain;charset=utf-8'})
//----内容类型
//text/html 可以解析返回中的HTML标签
//charset=utf8 可以解析中文。
res.writeHead(200, {'Content-Type': 'text/html;charset=utf8‘});
res.setHead( 'Content-Type','text/html;charset=utf8‘) 推荐
//文件类型
/**
text/plain 纯文本 如果不设置内容类型 默认是纯文本
text/html
text/css
application/javascript
image/jpeg
application/json
*/
在浏览器中没有Content-Type页面也是可以正常显示,这是因为我们使用的是高级浏览器,在使用低版本的浏览器则会有问题,所以一定指定返回资源的类型
accept= 'image/*'反映缓慢
input[type='file']的accept属性用来指定上传文件的MIME类型。
将其设为accept= 'image/*',顾名思义,过滤掉所有非图片文件,
但在实际操作中,发现有时会出现响应缓慢的问题,特别是在chrome与Safari,IE与firefox则没有此问题。
搜索过后发现: accept= 'image/*'会对每一个文件都进行一次遍历,在webkit内核的浏览器内校验时间较长,是此种内核浏览器的bug。
解决方法:将 * 通配符 修改成指定的MIME类型
例如: <input type="file" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg">
mime第三方模块
使用npm下载第三方模块
这个mime模块下有一个getType方法
mime.getType(路径)可以根据路径参数返回资源的类型
查询第三方模块的版本
npm list mime
请求方式
请求方式,是指前端和后端的交互手段,最早是不分get请求和post请求的发送请求都是一样,后来为了语义化做了一些区分,但他们的本质都是一样的,都是以请求报文的形式发送的
请求方式就是一种标识,就是用来告诉服务器端,当前这次请求要做的事件的类型
常见的请求方式
HTTP/1.0
1. GET : 偏向于获取的方式
+ 大部分都是给后端一些参数, 用来获取一些列数据
2. POST : 偏向于给服务器一些数据,添加数据
对于一些既不是添加数据也不是获取数据的操作就用post,比如说登陆操作,因为post操作相比较安全一些
+ 大部分都是登录, 给服务器一些信息服务器将这些信息存起来,然后服务器再给客户端一个简单的结果
我们目前只用 GET 和 POST
3:PUT 偏向于给服务器一些信息,但是是添加使用
大部分做注册,给服务器一些信息,你把这个信息存储起来
4:HEAD 用来获取服务器响应头信息
http/1.1
5: DELETE 偏向于删除
大部分用于删除评论,删除微博
6: CONNECT
管道连接改变代理连接使用
7:PATCH
偏向于给服务器一些信息,偏向于修改一些信息,大部分用于完善用户资料
8:OPTIONS:
用于获取服务器性能,但是需要服务端同意
get和post请求方式的区别(重点,面试常出)
get
1:语义:获取
向服务器发送get请求,最常讲的方式就是在浏览器地址栏中输入网址的方式,在服务器可以获取客户端的请求方式的可以通过服务器对象里的method属性获取
2:get携带参数的方式是queryString,查询字符串,get请求的请求参数是在地址栏中直接拼接,不在请求体里面
3:get和post的大小不一样:get理论上携带参数无限,但是因为地址栏有限,所以get请求参数一般都是2KB大小(ie的是2kb,所以一般get请求也都是2kb了)
4:get请求会被浏览器主动缓存
5:get是明文发送,post是暗文发送,相对于post来言get请求不安全
6:get请求只能发送url编码的数据(ASCII码),如果是中文会自动转码
post
1:语义:发送; 给
向服务器发送post请求,可以通过form标签的method属性,method=‘post’
2:post携带参数是requeryBody,在地址栏没有,在请求体里面
<form method='post' action='htttp://localhost:端口号'>
<input type='submit'>
</form>
开启服务器后点击提交按钮,服务器就会接到一个post请求,即是表单提交的请求,去还有一个get请求,是表单默认跳转发起的请求
设置get请求和post请求,服务器可以根据请求方式的不同响应不同的内容
3:post请求的请求参数,理论上大小也是无限,虽然post请求大小不受客户端限制,但是可能会被服务器端限制。
4:post请求不会被浏览器主动缓存,除非手动设置
5:post是暗文发送,Post请求相对于get请求比较安全。但是我们前端人员不聊安全,因为我们不配,前端的东西都是在明面上的,打开开发者工具就可以看到了
6:post理论上可以发送任意格式的数据,但是要和请求头里的content-type配套,如果不配套后端没有办法正常接收
请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
get请求
get请求的请求参数是在地址栏中传输
url: http://localhost:3000/?name=abc&age=123
const http = require('http')
const querystring = require('querystring') // 引用querystring
http.createServer((req, res) => {
console.log('url:',req.url) // 输出我们前端返回的链接
req.query = querystring.parse(req.url.split('?')[1]) // 设置query值,将req的url值进行分割整理
console.log(req.query)
res.end(JSON.stringify(req.query)) // 最后转换成字符串返回给前端
}).listen(3000, () => {
console.log('项目开启成功')
})
post请求
post请求的请求参数是在请求报文(请求体)中,FormData
post参数是通过事件的方式接收的:data事件 和 end事件
post参数在理论上数据量可以是无限的,为了减轻压力,他不是一次就接收完的,比如100M的数据可以分10次接收完,当有请求参数传输的时候就会触发data事件,当请求参数传递完的时候就会触发end事件
// post请求的数据 是添加到 请求体中
http.createServer((req, res) => {
// 解决跨域问题处理
res.setHeader("Access-Control-Allow-Origin", "*")
// 处理用户post 的请求
let body = '' // 变量body 用来接收存储用户传递的数据 因为post参数不是一次就传输完的,将每次触发data事件传入过来的参数与变量进行拼接,当传输完时触发end事件时输出这个变量
// 给req绑定on事件 监听data事件: 当有数据可读时触发。
//事件处理函数里参数就是传递过来的参数
req.on('data', (chunk) => {
body += chunk;
})
// 监听end 事件:没有更多的数据可读时触发。
req.on('end', ()=>{
//内置模块 querystring 可以处理这个格式的字符串,里面的parse方法可以将其转换为对象格式
// user=张三&pwd=123 ====> user:张三, pwd:123
body = querystring.parse(body);
console.log(body);
// 在服务器端给客户端设置一些不同的状态码
res.writeHead(404);
res.writeHead(200, {'Content-type':'text/plain;charset=utf-8'})
// 判断用户名或者密码是否正确
// 如果是前后端交互, 通过 res.write() 或者是 res.end() 方法 返回数据
// 每次客户端的请求服务器都要给予响应,否则客户端会一直处于等待的状态
res.write(`欢迎${body.user}`)
res.end();
})
}).listen(3000, () => {
console.log('server running ...');
})
静态资源
服务器不需要处理,可以直接响应给客户端的资源就是静态资源,例如 css,js,image,html文件
实际就是浏览器能直接运行的文件,无论谁每次请求得到的响应都是一样的
在服务端创建一个放置静态资源的文件夹,当客户端请求某个静态资源时,直接响应给他
动态资源
相同的请求地址,不同的响应资源,这响应结果就是动态资源
http://www.itals.cn/article?id=1
http://www.itals.cn/article?id=2
mime第三方模块
使用npm下载第三方模块
这个mime模块下有一个getType方法
mime.getType(路径)可以根据路径参数返回资源的类型
解决跨域问题
// 解决跨域问题处理
res.setHeader("Access-Control-Allow-Origin", "*")
路由
路由是指客户端请求地址与服务器程序代码的对应关系,简单说,就是请求什么就响应什么
路由实际就是一堆判断代码,判读客户端的请求的地址是什么然后调用服务端对应的处理代码