node.js学习笔记

运行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详解

ssh:什么是SSH 以及常见的ssh 功能

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';

 

学习文档:

Node.js URL模块的使用

url

node.js当中的http模块与url模块的简单介绍

 

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.分析"数据描述"

 

nodejs模块——fs模块 WriteFile写入文件

 

当使用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:

如何给老婆解释什么是RESTful

怎样用通俗的语言解释REST,以及RESTful?

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详解(三):深入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);

运行结果:

 关闭服务:

 

 

 

注:文件目录:

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值