运行node程序
1.进入目录
2.运行 node xx.js
3.新建service.js文件
const http = require("http");
let server = http.createServer(() => {
console.log('有人执行')
});
server.listen(8088);
4.运行 node server.js(ctrl+c可以关闭运行服务)
5.浏览器输入localhost:8088,按下回车键
6.注意:要是开发工具是vscode,并且嫌弃cmd进入命令窗口运行太麻烦,可以使用vscode运行,使用方法如下:
选中js文件,右键选中code run(vscode必须要安装code run拓展)即可运行js文件,浏览器输入localhost:8088,按下回车键,可以看到vscode控制台输出结果:(快捷键ctrl+alt+m可关闭服务)
注意:推荐使用cmd来运行js文件,不然有时候输入localhost:8088/x.html来访问文件会出错
md5加密(md5.js)
const crypto = require('crypto');
function md5(str) {
let obj = crypto.createHash('md5');
obj.update(str);
return obj.digest('hex');
}
console.log(md5('12345'))
运行结果
文件路径path(path.js)
const path = require('path');
var str = 'www/ddd/eww/1.txt'
console.log(path.dirname(str));//路径www/ddd/eww
console.log(path.basename(str));//1.txt
console.log(path.extname(str));//.txt
结果
进程和线程
进程拥有独立的执行空间、存储
同一个进程内的所有线程共享一套空间、代码
多进程(php,node)--- 成本高,慢,安全(进程间隔离),进程间通信麻烦,写代码简单
多线程(java,c)---成本低,快,不安全,(线程要死一起死),线程间通信容易,代码复杂
event事件队列(event.js)
const event = require('events').EventEmitter;
let ev = new event();
// 1.监听(接收)
ev.on('msg', function(a, b, c) {
console.log('收到了msg事件', a, b, c)
})
ev.emit('msg', 1, 2, 3);
// 2.派发(发送)
function msg(a, b, c) {
console.log('收到了msg事件', a, b, c)
}
msg(1, 2, 3);
ssl:SSL详解
TLS:SSL与TLS 区别 以及介绍 SSL与TLS的区别以及介绍
zlib
stream,
iPv4和ipv6:什么是IP?IPv4与IPv6的区别在哪里?
dns解析(dns.js)
const dns = require('dns');
dns.resolve('baidu.com', (err, res) => {
if (err) {
console.log('解析失败')
} else {
console.log(res)
}
})
http服务器
let http = require('http');
let server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', "text/palin;charset=utf-8");
res.end('hello node.js');
});
server.listen(8088, '127.0.0.2', () => {
console.log("服务器已运行,请打开浏览器,输入127.0.0.2:8088来运行");
})
运行:
例:(service3.js)
const http = require('http');
let server = http.createServer((req, res) => {
switch (req.url) {
case '/aaa':
res.write('abc');
break;
case '/bbb':
res.write('ddd');
break;
case '/1.html':
res.write('<html><head></head><body>fewqbf8</body></html>');
break;
default:
break;
}
res.end();
})
server.listen(8088)
上述代码在文件多时不好用,修改为(service4.js),与1.html搭配使用
const http = require('http');
const fs = require('fs');
let server = http.createServer((req, res) => {
fs.readFile(`www/${req.url}`, (err, data) => {
if (err) {
res.write('404');
} else {
res.write(data);//将data发送给客户端,下面的例子中data为1.html的内容<!DOCTYPE html>....</html>
}
res.end(); //因为异步会先执行end再执行write,所以end要放这里
})
})
server.listen(8088)
fs.readFile(path,[ options], callback),读取文件内容,callback有两个参数 (err,data),其中data是文件的内容。
在上面的例子中,fs.readFile(`www/${req.url}...表示读取www文件夹下所输入的url(1.html)的内容(即整个html文件的代码<!DOCTYPE> <html>...</html>),res.write(data);会将data发送给客户端
http服务器请求第三方网站:
const http = require('http');
//请求第三方网站
http.get('http://www.imooc.com/u/card', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
})
res.on('end', () => {
let result = data;
console.log(result);
})
})
运行:
get数据处理(service2.js)
数据存放在“req.url”里面
let http = require('http');
const urllib = require('url')
// get表单
let server = http.createServer((req, res) => {
let obj = urllib.parse(req.url, true);//返回一个Url对象,true表示会使用querystring模块来解析URL中的查询字符串部分,从而使obj的query部分变成对象形式
let { pathname, query } = obj;//query--解析url?后面的东西
console.log(obj);
console.log(pathname, query);
res.end();
});
server.listen(8088)
url.parse(urlStr, parseQueryString=false, slashesDenoteHost=false)方法用于解析URL对象,解析后返回一个JSON对象
当form表单输入:name:111,password:aaa时,
//form表单:
<form action="http://localhost:8088/api" method="get">
名字:<input type="text" id="name" name="name"><br> 密码:
<input type="text" name="password" id=""><br>
<input type="submit" value="提交">
</form>
req.url的值为:/api?name=111&password=aaa
obj的输出为:
pathname的值:/api
query的值:
当urllib.parse(req.url, true);时,query的值为{ name: '111', password: 'aaa' }
当urllib.parse(req.url, false);时,query的值为'name=111&password=aaa';
学习文档:
post数据处理
数据存放在body里面、比较大(service5.js)
const http = require('http');
const querystring = require('querystring');
let server = http.createServer((req, res) => {
let str = '';
req.on('data', data => {
str += data;
//data是buffer格式的数据,这里因为str += data,str为string类型,
//所以data通过.toString()方法将转换后的结果给str加上,str保持了string类型
});
// 结束了
req.on('end', () => {
let post = querystring.parse(str);
console.log(str, post);
})
res.end();
})
server.listen(8088)
nodejs用req.on(data)接收客户端的数据;request传递给处理程序的对象实现了ReadableStream接口,所以request对象就像任何其他流一样,可以在其他地方收听或传输此流。我们可以通过收听流'data'和'end'事件来直接从流中获取数据,每个'data'事件中发出的数据格式都是buffer。request流中的错误通过在流上发出'error'事件来呈现
当form表单输入:name:111,password:aaa时,
<form action="http://localhost:8088/api" method="post">
名字:<input type="text" id="name" name="name"><br> 密码:
<input type="text" name="password" id=""><br>
<input type="submit" value="提交">
</form>
str的值:name=111&password=aaa
post的值:{ name: '111', password: 'aaa' }
get和post合并处理(service6.js)
//get 和post一起处理
const http = require('http');
const url = require('url');
const querystring = require('querystring');
let server = http.createServer((req, res) => {
// get
let { pathname, query } = url.parse(req.url, true);
// post
let str = '';
req.on('data', data => {
str += data;
})
req.on('end', () => {
let post = querystring.parse(str);
console.log(pathname, query, post);
})
res.end();
})
server.listen(8088);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>get表单</title>
<style></style>
</head>
<body>
<div>
<form action="http://localhost:8088/reg" method="get">
用户名:<input type="text" name="user"><br> 密码:
<input type="password" name="pass">
<input type="submit">
</form>
</div>
</body>
</html>
运行截图:
代码进一步改进(和get表单.html配合):(service7.js)
//get 和post一起处理+接口设置
const http = require('http');
const url = require('url');
const querystring = require('querystring');
const fs = require('fs');
let users = {
'blue': '4324123'
}
let server = http.createServer((req, res) => {
// get
let { pathname, query } = url.parse(req.url, true);
// post
let str = '';
req.on('data', data => {
str += data;
})
req.on('end', () => {
let post = querystring.parse(str);
let { user, pass } = query;
//写东西
switch (pathname) {
case '/reg': //注册
if (!user) {
res.write('{"err":1,"msg":"user is required"}');
} else if (!pass) {
res.write('{"err":1,"msg":"pass is required"}');
} else if (!/^\w{8,32}$/.test(user)) {
res.write('{"err":1,"msg":"user is invalid"}');
} else if (/^['|"]$/.test(pass)) {
res.write('{"err":1,"msg":"password is invalid"}');
} else if (user[user]) {
res.write('{"err":1,"msg":"the same user"}');
} else {
user[user] = pass;
res.write('{"err":0,"msg":"success"}');
}
res.end();
break;
case '/login': //登陆
if (!user) {
res.write('{"err":1,"msg":"user is required"}');
} else if (!pass) {
res.write('{"err":1,"msg":"pass is required"}');
} else if (!/^\w{8,32}$/.test(user)) {
res.write('{"err":1,"msg":"user is invalid"}');
} else if (/^['|"]$/.test(pass)) {
res.write('{"err":1,"msg":"password is invalid"}');
} else if (!user[user]) {
res.write('{"err":1,"msg":"no such user"}');
} else if (user[user] != pass) {
res.write('{"err":1,"msg":"name/password wrong"}');
} else {
res.write('{"err":0,"msg":"success"}');
}
res.end();
break;
default:
fs.readFile('www/${pathname}', (err, data) => {
})
}
})
// res.end();
})
server.listen(8088);
运行截图:
fs.readFile:读取文件:
let http = require('http');
const urllib = require('url');
const fs = require('fs');
let server = http.createServer((req, res) => {
var pathname = urllib.parse(req.url).pathname;
pathname = '\server' + pathname; //需要转换地址为1.html所在目录地址,所以前面加上'\server'
fs.readFile(pathname, (err, data) => {
if (err) {
res.writeHeader(404, {
'Content-Type': 'text/html'
})
} else {
res.writeHeader(200, {
'Content-Type': 'text/html'
})
console.log(data.toString());
res.write(data.toString());
}
res.end();
})
});
server.listen(8088, '127.0.0.2', () => {
console.log("服务器已运行,请打开浏览器,输入127.0.0.2:8088来运行");
})
1.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>dsfhdsiof</title>
</head>
<body>
fdgklfd
</body>
</html>
运行:
数据库:
1.关系型数据库---Mysql/oracle 最常见、最常用(大型系统)
数据之间是有关系的
2.文件型数据库---sqlite
简单、小
3.文档型数据库---mangodb
直接存储异构数据--方便
表单的三种POST:
1.text/plain 用的很少,纯文字
2.application/x-www-form-urlencoded 默认 url编码方式,xxx=xxx&xxx=xx...
3.multipart/form-data 上传文件内容
互联网环境下传输的是二进制文件,所以表单上传的文件如果是txt文本文件,那就还好,不会有什么问题,但是要是上传图片或者媒体文件的话,这些文件都是二进制数据,将这些文件的内容像service6.js写的那样转成字符串,那么就会出乱子,根本没办法看,所以需要把二进制文件转化为buffer类型浏览器才能使用。
文件上传:
html代码(post表单.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>post表单</title>
<style></style>
</head>
<body>
<form action="http://localhost:8088/upload" method="post" enctype="multipart/form-data">
<input type="text" name="user">
<input type="password" name="pass">
<input type="file" name="f1">
<input type="submit" value="提交">
</form>
</body>
</html>
node代码:(server_post.js)
//和post.html合作处理文件上传
const http = require('http');
let server = http.createServer((req, res) => {
let arr = [];
req.on('data', data => {
arr.push(data);
});
req.on('end', () => {
let data = Buffer.concat(arr);
console.log(data)
res.end();
});
});
server.listen(8088);
表单提交数据,服务器收到的数据格式(以上传):
最初始的格式:
------WebKitFormBoundaryDqs4grRjEIPy5ZmY
Content-Disposition: form-data; name="user"
blue
------WebKitFormBoundaryDqs4grRjEIPy5ZmY
Content-Disposition: form-data; name="pass"
asdfasdf
------WebKitFormBoundaryDqs4grRjEIPy5ZmY
Content-Disposition: form-data; name="f1"; filename="a.txt"
Content-Type: text/plain
aaa
bbb
ccccccc
------WebKitFormBoundaryDqs4grRjEIPy5ZmY--
简化为:
<分隔符>\r\n
Content-Disposition: form-data; name="user"\r\n
\r\n
blue\r\n
<分隔符>\r\n
Content-Disposition: form-data; name="pass"\r\n
\r\n
asdfasdf\r\n
<分隔符>\r\n
Content-Disposition: form-data; name="f1"; filename="a.txt"\r\n
Content-Type: text/plain\r\n
\r\n
<文件内容>\r\n
<分隔符>--
简化为:
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n
<分隔符>--
注意:windows下的点一下回车,效果是:回车换行,就是\r\n
解析数据:
1.用"<分隔符>"切开数据
[
空,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
--
]
2.丢弃头尾元素
[
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
]
3.丢弃每一项的头尾\r\n
[
数据描述\r\n\r\n数据值,
数据描述\r\n\r\n数据值,
数据描述1\r\n数据描述2\r\n\r\n<文件内容>,
]
4.用第一次出现的"\r\n\r\n"切分
普通数据:[数据描述, 数据值]
或
文件数据:[数据描述1\r\n数据描述2, <文件内容>]
5.判断描述的里面有没有"\r\n"
有——文件数据:[数据描述1\r\n数据描述2, <文件内容>]
没有——普通数据:[数据描述, 数据值]
6.分析"数据描述"
当使用buffer时,对buffer数据进行:(buffer操作.js)
1.查找(indexOs) 2.截取(slice(s,e)[s,..e-1], splice(s) ) 3.切分(split)
let a = new Buffer('abb--bb--ijabgerbgu');
console.log(a.indexOf('--')); //3
console.log(a.slice(0, 3).toString()); //abb
//可能不带有split,需要自定义
Buffer.prototype.split = Buffer.prototype.split || function(b) {
let arr = [];
let cur = 0;
let n = 0;
while ((n = this.indexOf(b, cur)) != -1) {
arr.push(this.slice(cur, n))
cur = n + b.length;
}
arr.push(this.slice(cur))
return arr;
}
console.log(a.split('--').toString());//abb,bb,ijabgerbgu
加入buffer的node具体代码:(server_post.js)
//和post.html合作处理文件上传
const http = require('http');
const common = require('./libs/common');
const fs = require('fs');
const uuid = require('uuid/v4');
let server = http.createServer((req, res) => {
let arr = [];
req.on('data', data => {
arr.push(data);
});
req.on('end', () => {
let post = {};
let files = {};
let data = Buffer.concat(arr);
//解析二进制文件上传数据
if (req.headers['content-type']) {
//req.headers['content-type']的值:multipart/form-data;boundary=----WebKitFormBoundary2HXD5KenB2c31hU4
let str = req.headers['content-type'].split(';')[1]; //str的值:boundary=----WebKitFormBoundary2HXD5KenB2c31hU4
if (str) {
let boundary = '--' + str.split('=')[1]; //加上--是因为这两个是标志,和最后面的--是对应的
//1.用分隔符切分整个数据(这里不懂就结合上面解析数据的说明看)
let arr = data.split(boundary);
// 2.丢弃头尾两个数据
arr.shift();
arr.pop();
// 3.丢弃每个数据头尾的\r\n
arr = arr.map(buffer => buffer.slice(2, buffer.length - 2));
// 4.每个数据在第一个连续的\r\n处切割成两半
arr.forEach(buffer => {
let n = buffer.indexOf('\r\n\r\n');
let disposition = buffer.slice(0, n);
let content = buffer.slice(n + 4);
disposition = disposition.toString();
// disposition的值为:Content-Disposition: form-data; name="xxx"
if (disposition.indexOf('\r\n') == -1) { //普通数据
content = content.toString();
let name = disposition.split(';')[1].split('=')[1];
//name的值为"xxx"
name = name.substring(1, name.length - 1); //name的值为xxx,去掉了引号
// 5.完成
post[name] = content;
// post的值类似这样{ user: '121' }
} else { //文件
// disposition的值为: Content - Disposition: form - data;name = "f1";filename = "key.txt"
// Content - Type: text / plain
let [line1, line2] = disposition.split('\r\n');
// line1的值:Content-Disposition: form-data; name="f1"; filename="key.txt"
// line2的值:Content-Type: text/plain
let [, name, filename] = line1.split(';');
let type = line2.split(':')[1];
name = name.split('=')[1];
name = name.substring(1, name.length - 1);
filename = filename.split('=')[1];
filename = filename.substring(1, filename.length - 1);
//使用uuid为文件取名,保证不会重名
let path = `upload/${uuid().replace(/\-/g,'')}`;
fs.writeFile(path, content, (err => {
if (err) {
console.log('文件写入失败')
} else {
files[name] = { filename, path, type };
//content为Buffer格式,包含文件内容,files的值如下:
// { f1:
// { filename: 'key.txt',
// path: 'upload/ace9c2512dca4c7b9db2e92e7c1e86ab',
// type: ' text/plain' } }
}
}));
}
});
}
}
res.end();
});
});
server.listen(8088);
运行结果:upload文件夹成功接收上传文件
上述代码的瑕疵:会等到所有数据都到达了才开始处理 改进:收到一部分就解析一部分 极大节约内存
2.fs.readFile
fs.writeFile
readFile先把所有数据全读到内存中,然后回调:
1.极其占用内存
2.资源利用极其不充分
所以使用流(读一点、发一点)可以完成上述改善
流的三种类型:
1.读取流 fs.createReadStream、req
2.写入流 fs.createWriteStream、res
3.读写流 压缩、加密
压缩,例:(zlib.js)
const zlib = require('zlib');
const fs = require('fs');
let rs = fs.createReadStream('jquery-3.2.1.min.js');
let ws = fs.createWriteStream('jquery-3.2.1.min.js.gz');
let gz = zlib.createGzip();
rs.pipe(gz).pipe(ws);
ws.on('finish', () => {
console.log('完成')
})
运行结果:
流,例子:(server_stream.js)
const http = require('http');
const fs = require('fs');
let server = http.createServer((req, res) => {
let rs = fs.createReadStream(`www/${req.url}`);
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('not found');
res.end();
})
});
server.listen(8088);
读取成功,截图:
读写+压缩:(server_stream_gz.js)
const http=require('http');
const fs=require('fs');
const zlib=require('zlib');
let server=http.createServer((req, res)=>{
let rs=fs.createReadStream(`www${req.url}`);
//rs.pipe(res);
res.setHeader('content-encoding', 'gzip');
let gz=zlib.createGzip();
rs.pipe(gz).pipe(res);
rs.on('error', err=>{
res.writeHeader(404);
res.write('Not Found');
res.end();
});
});
server.listen(8088);
压缩前:
压缩后:
服务端怎么判断文件类型:判断拓展名、分析文件结构
缓存:
第一重要、缓存策略:
cache-control
expires
第二重要、缓存实现过程:
1.第一次S->C:"Last-Modified: Sat, 02 Dec 2017 04:03:14 GMT"
2.第二次C->S:"If-Modified-Since: Sat, 02 Dec 2017 04:03:14 GMT"
3.第二次S->C:200 || 304
使用Last-Modified,代码:(huan_server.js)
//缓存的server
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req, res) => {
let { pathname } = url.parse(req.url);
//获取文件日期
fs.stat(`www/${pathname}`, (err, stat) => {
if (err) {
res.writeHeader(404);
res.write('not found');
res.end();
}
let rs = fs.createReadStream(`www${pathname}`);
res.setHeader('Last-Modified', stat.mtime.toGMTString());
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('not found');
res.end();
});
})
}).listen(8088);
运行结果
第一次请求:
刷新界面,第二次:
代码完善状态码变化:(huan_server.js)
//缓存的server
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req, res) => {
let { pathname } = url.parse(req.url);
//获取文件日期
fs.stat(`www/${pathname}`, (err, stat) => {
if (err) {
res.writeHeader(404);
res.write('not found');
res.end();
}
if (req.headers['if-modified-since']) {
let oDate = new Date(req.headers['if-modified-since']);
let time_client = Math.floor(oDate.getTime() / 1000);
let time_server = Math.floor(stat.mtime.getTime() / 1000);
if (time_server > time_client) { //服务器的文件时间>客户端手里的版本
sendFileToClient();
} else {
res.writeHeader(304);
res.write('not modified')
res.end();
}
} else {
sendFileToClient();
}
function sendFileToClient() {
//发送
let rs = fs.createReadStream(`www/${pathname}`);
res.setHeader('Last-Modified', stat.mtime.toGMTString());
//输出
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('not found');
res.end();
});
}
})
}).listen(8088);
运行结果:
刷新界面:
修改文件(1.html内容),状态码会变回200,然后刷新,又会变回304
代码加上cache-control:(huan_server.js)
//缓存的server
const http = require('http');
const fs = require('fs');
const url = require('url');
http.createServer((req, res) => {
let { pathname } = url.parse(req.url);
//获取文件日期
fs.stat(`www/${pathname}`, (err, stat) => {
if (err) {
res.writeHeader(404);
res.write('not found');
res.end();
}
if (req.headers['if-modified-since']) {
let oDate = new Date(req.headers['if-modified-since']);
let time_client = Math.floor(oDate.getTime() / 1000);
let time_server = Math.floor(stat.mtime.getTime() / 1000);
if (time_server > time_client) { //服务器的文件时间>客户端手里的版本
sendFileToClient();
} else {
res.writeHeader(304);
res.write('not modified')
res.end();
}
} else {
sendFileToClient();
}
function sendFileToClient() {
//发送
let rs = fs.createReadStream(`www/${pathname}`);
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Last-Modified', stat.mtime.toGMTString());
//输出
rs.pipe(res);
rs.on('error', err => {
res.writeHeader(404);
res.write('not found');
res.end();
});
}
})
}).listen(8088);
改变1.html的内容,运行服务器(node huan_server.js),刷新界面:
多线程:性能高;复杂、考验程序员
多进程:性能略低;简单、对程序员要求低
Node.js默认:单进程、单线程
主进程:负责派生子进程
子进程:干活
进程-怪:
1.普通程序不能“创建”进程,只有系统才能创建进程,只有主进程能分裂
2.进程是分裂出来的
3.分裂出来的两个进程执行的是同一套代码
4.父子进程之间可以共享“句柄”
多个进程:第一个满了 -> 启用第二个 -> 前两个都满了 -> 启用第三个
进程例子:cluster:(cluster.js)
const cluster = require('cluster');
const os = require('os');
const http = require('http');
const process = require('process');
if (cluster.isMaster) { //只有主进程可以fork
// os.cpus()是一个cpu数组
for (let i = 0; i < os.cpus().length; i++) {
cluster.fork();
}
console.log('主进程')
} else {
let server = http.createServer((req, res) => {
console.log(process.pid); //当前进程
res.write('aaaaa');
res.end();
});
server.listen(8088);
console.log('服务器开好了,在8088上')
}
浏览器输入localhost:8080/1.html,运行:
数据库:
1.服务端:wamp里面的mysql、单装的MySQL的server
2.客户端:Nodejs、Java、PHP、Navicat for MySQL
认识数据库:
1.库-文件夹:不能存数据,只能管理表
2.表-文件:存数据
字段类型:
数字
整数:tinyint(8位)、smallint、mediumint、int、 bigint
| | |
-128~127|0~255 -21亿~21亿|0~43亿 18万万亿
小数:float-单精度浮点数、double-双精度浮点数
小数点后8位 10^308
字符串
小:varchar 255
大:text 1G
表情/图片:文件路径
数据库操作:
1.连接
let db=mysql.createConnection({host, port, user, password, database});
2.查询
db.query('干啥', (err, data)=>{});
SQL:
4大查询
1.增 INSERT
INSERT INTO 表 (字段列表) VALUES(值列表)
INSERT INTO user_table (ID, name, gender, chinese, math, english) VALUES(0, 'blue', '男', 35, 18, 29);
2.删 DELETE
DELETE FROM 表 WHERE 条件
DELETE FROM user_table WHERE ID=3;
3.改 UPDATE
UPDATE 表 SET 字段=值, 字段2=值2, ... WHERE 条件
UPDATE user_table SET chinese=100 WHERE ID=2;
4.查 SELECT
SELECT 字段列表 FROM 表 WHERE 条件
SELECT name, gender FROM user_table WHERE ID=2;
例(需要先npm install mysql -D安装):(mysql.js):
const mysql = require('mysql');
let db = mysql.createConnection({ host: 'localhost', user: 'root', password: '123', port: 3306, database: '开课吧' })
db.query(`INSERT INTO user (ID, name, gender, chinese, math, english) VALUES(4, '小明', '男', 98, 5, 3);`, (err, data) => {
if (err) {
console.log('错了', err);
} else {
console.log(data);
}
});
db.query(`DELETE FROM user WHERE ID=3;`, (err, data) => {
if (err) {
console.log('错了', err);
} else {
console.log(data);
}
});
运行结果:
node中mysql写法
let db=mysql.createConnection({配置});
let db=mysql.createPool({配置});
db.query(sql, (err, data)=>{});
连接池:当服务器只有一个数据库时,如果有多个数据库操作在进行,某一个数据库操作很慢时,服务器可能会很卡住,要等这个操作进行完才能进行下一个操作,而解决这种问题的方法就是连接池,连接池里面存放多个同时开启的连接,需要使用时就取出一个可用的连接,用完就放回,这样可以减少被卡住的几率
例:(mysql2.js),运行结果与mysql.js大体相同
const mysql = require('mysql');
let db = mysql.createPool({ host: 'localhost', user: 'root', password: '123', port: 3306, database: '开课吧' })
db.query(`INSERT INTO user (ID, name, gender, chinese, math, english) VALUES(1, '小明', '男', 98, 5, 3);`, (err, data) => {
if (err) {
console.log('错了', err);
} else {
console.log(data);
}
});
db.query(`DELETE FROM user WHERE ID=0;`, (err, data) => {
if (err) {
console.log('错了', err);
} else {
console.log(data);
}
});
RESTful:
接口
注册:
/reg?user=xxx&pass=xxx
=>{err: 0, msg: '原因'}
登陆:
/login?user=xxx&pass=xxx
=>{err: 0, msg: '原因'}
代码:(mysql.js+2.html)
const http = require('http');
const mysql = require('mysql');
const fs = require('fs');
const url = require('url');
const zlib = require('zlib');
const crypto = require('crypto'); //提供通用的加密和哈希算法
function md5(str) {
let obj = crypto.createHash('md5'); //创建一个hash实例
obj.update(str); //通过hash.update('需要机密的字符串')函数,实现加密。
return obj.digest('hex'); //通过hash.digest()函数实现字符串加密返回,默认返回的是2进制的数据,加入'hex'参数设置以16进制的形式显示出来
}
const _key = 'oweiohgwryt';
function md5_2(str) {
return md5(md5(str) + _key);//用于给密码加密
}
let db = mysql.createPool({ host: 'localhost', port: 3306, user: 'root', password: '123', database: '开课吧' });
let server = http.createServer((req, res) => {
let { pathname, query } = url.parse(req.url, true);
let { user, pass } = query;
switch (pathname) {
//接口
case '/reg':
//校验
if (!user) {
res.write('{"err":1,"msg":"username can\'t be null"}');
res.end();
} else if (!pass) {
res.write('{"err":1,"msg":"password can\'t be null"}');
res.end();
} else if (!/^\w{4,16}$/.test(user)) {
res.write('{"err":1,"msg":"user is invaild"}');
res.end();
} else if (/['|"]/.test(pass)) {
res.write('{"err":1,"msg":"password is invaild"}');
res.end();
} else {
db.query(`SELECT * FROM student WHERE user='${user}';`, (err, data) => {
if (err) {
res.write('{"err":1,"msg":"database error"}');
res.end();
} else if (data.length > 0) {
res.write('{"err":1,"msg":"username exists"}');
res.end();
} else {
db.query(`INSERT INTO student (ID,user,password) VALUES(0,'${user}','${md5_2(pass)}');`, (err, data) => {
if (err) {
res.write('{"err":1,"msg":"database error"}');
res.end();
} else {
res.write('{"err":0,"msg":"success"}');
res.end();
}
})
}
})
}
break;
case '/login':
//校验
if (!user) {
res.write('{"err":1,"msg":"username can\'t be null"}');
res.end();
} else if (!pass) {
res.write('{"err":1,"msg":"password can\'t be null"}');
res.end();
} else if (!/^\w{4,16}$/.test(user)) {
res.write('{"err":1,"msg":"user is invaild"}');
res.end();
} else if (/['|"]/.test(pass)) {
res.write('{"err":1,"msg":"password is invaild"}');
res.end();
} else {
db.query(`SELECT * FROM student WHERE user='${user}';`, (err, data) => {
if (err) {
res.write('{"err":1,"msg":"database error"}');
res.end();
} else if (data.length == 0) {
res.write('{"err":1,"msg":"user not exists"}');
res.end();
} else if (data[0].password != md5_2(pass)) {
res.write('{"err": 1, "msg": "username or password is incorrect"}');
res.end();
} else {
res.write('{"err": 0, "msg": "success"}');
res.end();
}
})
}
break;
default:
//静态文件
let rs = fs.createReadStream(`www/${pathname}`);
let gz = zlib.createGzip();
res.setHeader('content-encoding', 'gzip');
rs.pipe(gz).pipe(res);
rs.on('error', err => {
res.writeHead(404);
res.write('not found');
res.end();
})
break;
}
});
server.listen(8088);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>mysql登陆注册</title>
<script src="./jquery-3.2.1.min.js"></script>
<script>
$(function() {
$("#btn_reg").click(function() {
$.ajax({
url: '/reg',
dataType: 'json',
data: {
user: $('#user').val(),
pass: $('#pass').val()
},
success(json) {
if (json.err) {
alert('注册失败' + json.msg);
} else {
alert('注册成功');
}
},
error(err) {
alert('失败');
}
});
});
$("#btn_login").click(function() {
$.ajax({
url: '/login',
dataType: 'json',
data: {
user: $('#user').val(),
pass: $('#pass').val()
},
success(json) {
if (json.err) {
alert('登陆失败' + json.msg);
} else {
alert('登陆成功');
}
},
error(err) {
alert('失败' + err);
}
})
})
})
</script>
</head>
<body>
<input type="text" name="" id="user"><br>
<input type="password" name="" id="pass"><br>
<input type="button" value="注册" id="btn_reg">
<input type="button" value="登陆" id="btn_login">
</body>
</html>
运行结果:
数据库:
WebSocket:
1.双向通信
2.自动跨域
3.性能高
socket.io-----一个基于 Node.js 的实时应用程序框架,主要使用websocket协议,支持 websocket、polling 两种数据传输方式以兼容浏览器不支持 WebSocket 场景下的通信需求。
例(需要先npm install socket.io -D):(ws.js+ws.html)
const http = require('http');
const io = require('socket.io');
let server = http.createServer();
server.listen(8088);
let wsServer = io.listen(server);
wsServer.on('connection', sock => { //当有人连接时
//接收
// sock.on('aaa', function(a, b, c) {
// console.log(a, b, c);
// })
setInterval(function() {
sock.emit('t', new Date().getTime());
}, 2000);
})
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>websocket例子</title>
<style></style>
<script src="http://localhost:8088/socket.io/socket.io.js"></script>
<!-- websocket库提供 -->
<script>
let sock = io.connect('ws://localhost:8088');
sock.on('t', function(data) {
console.log(data);
})
</script>
</head>
<body>
</body>
</html>
结果:
websocket聊天室例子(ws_server.js+ws_server.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>websocket聊天室例子</title>
<style>
.me {
color: pink;
}
.err {
width: 100%;
height: 20px;
line-height: 2px;
text-align: center;
color: red;
display: none;
}
</style>
<script src="http://localhost:8088/socket.io/socket.io.js" charset="utf-8"></script>
<!-- websocket库提供 -->
<script>
let sock = io.connect('ws://localhost:8088');
sock.on('connect', () => {
document.getElementsByClassName('err')[0].style.display = 'none';
})
sock.on('disconnect', () => {
document.getElementsByClassName('err')[0].style.display = 'block';
})
//聊天室
window.onload = function() {
let oTxt = document.getElementById('txt1');
let oBtn = document.getElementById('btn1');
let oUl = document.getElementById('ul1');
oBtn.onclick = function() {
sock.emit('msg', oTxt.value);//发送信息
let oLi = document.createElement('li');
oLi.innerHTML = oTxt.value;
oLi.className = 'me';
oUl.appendChild(oLi);
}
sock.on('msg', str => {
let oLi = document.createElement('li');
oLi.innerHTML = str;
oUl.appendChild(oLi);
})
}
</script>
</head>
<body>
<div class="err">
无法连接到服务器,请检查网络
</div>
<ul id="ul1" style="width:400px;height:300px; border:1px solid pink; overflow:auto;">
</ul>
<textarea id="txt1" cols="80" rows="4"></textarea>
<input type="button" value="发送" id="btn1">
</body>
</html>
const http = require('http');
const io = require('socket.io');
let server = http.createServer((req, res) => {
});
server.listen(8088);
let aSock = [];
let wsServer = io.listen(server);
wsServer.on('connection', sock => { //当有人连接时
aSock.push(sock);//放入sock实例
//接收
sock.on('msg', str => {
aSock.forEach(s => {
if (s != sock) { //等于说明是它自己,消息要发给其他人
s.emit('msg', str);
}
})
})
//断开连接,就删除,比如我和别人在聊天,我关闭聊天窗口了,那么就把我删除
sock.on('disconnect', () => {
let n = aSock.indexOf(sock);
if (n != -1) {
aSock.splice(n, 1);
}
})
})
setInterval(function() {
console.log(aSock.length);
})
运行结果:
关闭服务:
服务端:
sock.on('connection')//连接
sock.on('disconnect')//断开
客户端:
sock.on('connect')//连接
sock.on('disconnect')//断开
原生websocket
1.只有前台有WebSocket这个东西
2.后台没有,后台有Socket
websocket连接/发送数据过程:
1.首先发送一部分普通的http数据(websocket是基于http的),在这过程中会传递一个“密钥”过来,作连接的用处而非保密
2.协议升级,将普通的http协议升级成websocket协议
3.之后的数据就全部变成了websocket数据
学习资料:
WebSocket详解(三):深入WebSocket通信协议细节
http发过来的数据例子:
用WebSocket:
1.socket.io
2.原生WebSocket
i.net模块
ii.流程
a.握手
1. Client提供:version:13、sec-websocket-key:xxxxx
2. 进行:sha1(key+mask)=>base64,数据还给Client
3.S:101 Switching Protocols、sec-websocket-accept: base64
4.结果: C <-> S
Client:(涉及事件)
onopen
onmessage
onclose
Server:(涉及事件)
net.createServer(sock=>{});
sock.once('data', 握手);
sock.on('data', 数据请求);
sock.on('end');
b.数据帧解析
例:(native_ws.js+native_ws.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>原生websocket</title>
<style></style>
<script>
let sock = new WebSocket('ws://localhost:8088');
// 连上了
sock.emit = function(name, ...args) {
alert(JSON.stringify({
name,
data: [...args]
}));
sock.send(JSON.stringify({
name,
data: [...args]
}))
}
sock.onopen = function() {
alert('连接上了');
sock.emit('msg', 1, 2, 3)//发送数据
};
// 有数据过来
sock.onmessage = function() {
alert('有数据过来');
};
// 断开了
sock.onclose = function() {
alert('断开了');
};
</script>
</head>
<body>
<div>
</div>
</body>
</html>
//原生websocket
const http = require('http');
const net = require('net'); //提高TCP操作的库 原生Socket
const crypto = require('crypto');
//无法使用httpServer,因为推过来的是ws请求,而它只能处理http请求
let server = net.createServer(sock => {
//数据过来——握手只有一次
sock.once('data', (data) => {
console.log('握手开始');
//以下都是升级数据:
let str = data.toString(); //http发过来的头
let lines = str.split('\r\n');
//舍弃第一行(数据没什么用)和最后两行(没有信息)
lines = lines.slice(1, lines.length - 2);
let headers = {};
lines.forEach(line => {
let [key, val] = line.split(': ');
headers[key.toLowerCase()] = val;
})
if (headers['upgrade'] != 'websocket') {
console.log('其他协议', headers['upgrade']);
sock.end();
} else if (headers['sec-websocket-version'] != 13) {
console.log('版本不对', headers['sec-websocket-version']); //支持的Websocket版本,目前必须只能是13,之前版本已弃用
sock.end();
} else {
let key = headers['sec-websocket-key'];
let mask = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; //作者规定的一个值,用于后续版本升级的识别
// sha1(key+mask)->base64=>client,作者规定必须这样
let hash = crypto.createHash('sha1');
hash.update(key + mask);
let key2 = hash.digest('base64');
sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${key2}\r\n\r\n`);
//握手结束
console.log('握手结束');
//真正的数据
sock.on('data', data => {
console.log('有数据');
console.log(data);
let FIN = data[0] & 0x001;
let opcode = data[0] & 0x0F0;
let mask = data[1] & 0x001;
let payload = data[1] & 0x0FE;
})
}
})
// 断开
sock.on('end', () => {
console.log('客户端已断开');
})
})
server.listen(8088);
运行结果:
关闭服务:
注:文件目录: