协议就是数据封装格式+传输 , 数据的发送是一直封装数据,数据接受方一直都是解封
是很多协议的集合
作者:Gen_
链接:https://www.jianshu.com/p/26e0bbb64b4e
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
交换机是靠mac地址工作的
mac地址在内嵌在网卡,全球唯一,6个字节,前3个表示厂商 ,交换机是找IP地址
路由就是在线路中选择一条最短的 。路由器是转发IP地址的,交换机属于数据链路层
const net = require("net");
// 创建基于流的 TCP 可以进程间通信的服务器和客户端
const server = net.createServer(function (socket) {
// 最大连接2个客户端
// server.maxConnections = 2;
server.getConnections((err, count) => {
console.log("hello", count)
})
socket.on("data", function (data) {
console.log(data.toString())
});
socket.on("error", function (data) {
console.log('error')
});
// 客户端开始关闭
socket.on("end", function (data) {
console.log("client end")
// 当所有的客户端断开连接后,服务器停止服务
server.unref();
});
// 客户端关闭
socket.on("close", function (data) {
console.log("client close")
})
// server close 方法 不在接受新的连接 旧的连接可以继续服务
setTimeout(() => {
server.close();
})
});
server.listen(8989, function () {
console.log("server started")
})
server.on("close", function () {
console.log("server close")
})
server.on("error", function () {
console.log("server error")
})
传输效率高,速度快,不可靠的,无连接的协议
// 一个 byte 是 由 8个bit组成, 可表示256种状态
let a = 0b10100;//二进制
let b = 0o24;//八进制
let c = 20;//十进制
let d = 0x14;//十六进制
console.log(a == b);
console.log(b == c);
console.log(c == d);
// 转8进制
console.log(c.toString(8))
console.log(c.toString(2))
// 把输入按照进制转换成10进制的数字
console.log(parseInt('10100', 2));
// ASCII 码 第一位是0 ,其余7位表示不同的字符
// gb2312 如果小于127还是原来的,大于127 ,两个字节表示一个汉字
// Unicode (16进制) 保持其原编码不变,只是将其长度由原来的 8 位扩展为16 位
// UTF-8 (2进制) 就是在互联网上使用最广的一种 Unicode 的实现方式 utf8变长,英文一个字节,汉字3个
// 汉字3个字节 ['1110xxxx', '10xxxxxx', '10xxxxxx']
libuv内部是多线程的线程池和同步IO模拟一个异步的
eventLoop:function(){
// 如果队列不为空,就继续循环
while(this.globalEventQueue.length > 0){
// 从队列的头部拿出一个事件
var event = this.globalEventQueue.shift();
// 如果是耗时任务
if(isIOTask(event)){
// 从线程池里拿出一个线程
var thread = getThreadFromThreadPool();
// 交给线程处理
thread.handleIOTask(event)
}else {
// 非耗时任务处理后,直接返回结果
var result = handleEvent(event);
// 最终通过回调函数返回给V8,再由V8返回给应用程序
event.callback.call(null,result);
}
}
}
-> 否 -> 查找文件模块 -> 缓存文件模块 -> 返回exports
require -> 是否在模块缓存中 -> 否 -> 是否原生模块 -> 是 -> 是否在原生模块缓存区 -> 是 -> 返回exports
-> 否 -> 加载原生模块 -> 缓存原生模块 -> 返回exports
-> 是 -> 返回exports
// 效果是一样的
this.name = 'haha'
module.name = 'haha'
// 文件内的module对象
module.id // 主文件id是. , 其余为模块的文件名
module.path // 模块的查找路径
// require
// node加载文件是同步的,因为会缓存模块的export对象,所以一个模块加载多次是没用的
console.log(require.cache)
// resolve 方法解析一个模块的绝对路径,但是又不加载执行它
console.log(require.resolve("./module.js"))
// 主模块
console.log(require.main)
// require方法实现
const fs = require("fs");
const path = require("path");
let math = get("./math.js");
function get(url) {
const content = fs.readFileSync(path.join(__dirname, url));
const fn = new Function('exports', 'require', 'module', '__filename', '__dirname', content + '\n return module.exports;')
let module = {
exports: {}
}
return fn(module.exports, get, module, __filename, __dirname)
}
console.log(math)
// 不加参数默认参默认就是 [node地址,文件地址]
process.argv.forEach(function(item){
console.log(item);
});
process.on('exit',function(){
console.log('clear');
});
process.on('uncaughtException',function(err){
console.log(err);
})
// change工作目录
process.chdir("../../")
// 返回用户环境的变量
console.log(process.env)
新建一个文件 hello, 无后缀
#!/usr/bin/env node
console.log('hello');
console.log('hello ',process.argv);
// 给可执行权限
chmod 755 hello
// 在package.json 里声明
{
"bin":{
"hello":"hello"
}
}
npm link
hello
命令 let {exec} = require('child_process');
let child = exec('echo hello 参数是'+name,(err,stdout,stderr)=>{
if(err) throw err;
console.info(stdout);
});
curl是一种命令行工具,作用是发出网络请求,然后得到和提取数据
curl www.sina.com
// 保存网页
curl -o index.html www.sina.com
// 分配6个字节的buffer
const buf1 = Buffer.alloc(6)
// 分配一块没有初始化的内存
let buf2 = Buffer.allocUnsafe(6)
// console.log( Buffer.from("花1"))
//
buf1.fill( 'a',1, 3)
// console.log(buf1)
// 内容 开始index 长度
buf1.write("张玉华", 0 , 6, 'utf-8')
//console.log(buf1.toString())
const buff = Buffer.alloc(6);
// 在其中一位 填充一个8位的数字
buff.writeInt8(127, 0)
buff.writeInt8(-127, 1)
//console.log(buff)
const buff1 = Buffer.alloc(16);
// BE是大头在前,高位在前 LE是小头在前
buff1.writeInt16BE(256, 0)
console.log(buff1)
console.log(buff1.toString())
// read 也要 按照大头小头相应读
// slice 是浅拷贝
let buf4 = Buffer.alloc(6, 1);
let buf41 = buf4.slice(2, 5)
let {StringDecoder}= require("string_decoder");
let buf = Buffer.from("张玉华")
let buf1 = buf.slice(0, 5)
let sd = new StringDecoder()
console.log(buf1.toString()) // 张* 乱码
// write 读取内容,返回一个字符串
// write 判断buffer是不是一个字符,不是的话,缓存,放到下个字符串
console.log(sd.write(buf1)) // 张
let bf1 = Buffer.from("上海");
let bf2 = Buffer.from("徐家汇");
console.log(bf1)
console.log(bf1.length)
bf1.forEach(item => {
// log 输出都是10进制
console.log(item)
})
let bf = Buffer.concat([bf1, bf2])
console.log(bf.toString())
有三个执行者,所有者,所属组,其他用户,如 777 就是所有权限
读 | 写 | 执行 |
---|---|---|
r | w | x |
4 | 2 | 1 |
readFile会一次性读入到内存中
// 从当前路径解析出一个绝对路径
console.log(path.resolve("huahua"))
const fs = require("fs")
// fs 不加第二个参数。默认读取文件是buffer
let conf = {
encoding: "utf8",
flag: 'r'
}
fs.readFile("./code.js", conf, (err, data) => {
if (err) {
console.log(err)
} else {
console.log(data)
}
})
let conf1 = {
encoding: "utf8",
flag: 'a', // a表示追加编码
mode: '644'
}
fs.writeFile("hua.txt", 'hello', conf1, (err, data) => {
})
// 文件内容追加 用 fs.appendFile 是一样的
// 上述都是把文件作为一个整体操作,如果文件size大于内存,就无法执行
// fd 文件描述符
// 0 标准输入 1 标准输出 2 错误输出
fs.open("./code.js", "r", 0o666, function(err, fd){
console.log(fd)
})
// 监控文件变化
fs.watchFile("./demo.js", function(next, prev){
if(Date.parse(prev.ctime) == 0){
console.log("新增文件")
}else if(Date.parse(prev.ctime) !== Date.parse(next.ctime)){
console.log("修改文件了")
}else if(Date.parse(next.ctime) == 0){
console.log("删除文件了")
}
})
function copy(src, dist){
const SIZE = 3;
// 以只读的方式打开文本文件,文件必须存在
fs.open(src, "r", 0o666, function(err, readFD){
// w 以只写的方式打开文本文件,文件若存在则清空文件内容从文件头部开始写,若不存在则根据文件名创建新文件并只写打开
fs.open(dist, 'w', 0o666, function(err, writeFD){
let buff = Buffer.alloc(SIZE);
function next(err){
if(err){
console.log(err)
return;
}
fs.read(readFD, buff, 0, SIZE, null, function(err, bytesRead, buff){
if(bytesRead > 0){
fs.write(writeFD, buff, 0, bytesRead, null, next)
// 强行把缓存区写入文件,并且关闭
fs.fsync(fd, function(err){
fs.close(() => {console.log('close')})
})
}
})
}
next();
})
})
}
function myWrite(){
fs.open("./hua.txt", 'w', 0o666, function(err, fd){
let _bf = Buffer.from("玉华");
// write 写文件时,会先写入缓存区,再写入物理文件
fs.write(fd, _bf , 0 , 3, null, (err, byteWrite, buff) => {
fs.write(fd, _bf , 3 , 3, null, (err, byteWrite, buff) => {
fs.fsync(fd, () => {
fs.close(fd, () => {
console.log("关闭文件")
})
})
});
});
})
}
// 截断文件,保留一部分
function myDemo1(){
fs.open("aaa.hh", "r", 0o666, function(err, readFD){
if(err){
console.log(err)
}
})
}
function makeDir(path){
path = path.split("/")
function next(index){
console.log(index)
if(index > path.length){
return;
}
let cur = path.slice(0, index).join("/")
fs.access(cur,function(err){
if(err){
fs.mkdir(cur, 0o666, () => next(++index))
}else {
next(++index)
}
})
};
next(1)
}
makeDir("a/b/c")
function rmDirS(dir) {
const fileList = fs.readdirSync(dir);
fileList.forEach(item => {
const _path = dir + '/' + item;
if (data = fs.statSync(_path).isFile()) {
fs.unlinkSync(_path)
} else {
rmDirS(_path)
}
})
fs.rmdirSync(dir)
}
// 遍历一个文件夹,通常是深度优先 + 前序遍历
function removeFolder(dir) {
return new Promise(function (resolve, reject) {
fs.stat(dir, (err, data) => {
if(err){
reject(err)
}
if(!data){
reject()
}
if (data.isFile()) {
fs.unlink(dir, resolve)
} else {
fs.readdir(dir, (err, files) => {
if (err) {
reject(err)
}
Promise.all(files.map(file =>removeFolder(dir + '/' + file))).then(res => {
fs.rmdir(dir, resolve)
})
})
}
});
})
}
默认node不支持uft8 ,iconv把gbk转成utf8
const fs = require("fs")
const iconv = require('iconv-lite');
fs.readFile("./gbk.txt", function(err,data){
let str = iconv.decode(data, 'gbk')
console.log(str)
})
一般使用深度优先前序遍历
const fs = require("fs");
const option = {
highWaterMark: 3,
flags: 'r',
start: 2,
end: 9,
mode: 0o666
};
// 默认是返回 buffer
let rs = fs.createReadStream("./hua.txt", option);
// 文件流额外两个事件 open close
rs.on("open", () => {
console.log("open")
})
// 监听data事件,按照缓冲区大小,来读取数据
// 这个事件是一直进行的
rs.on("data", function(data){
console.log(data.toString())
// 暂停读取和发射data事件
rs.pause()
setTimeout(() => {
rs.resume()
}, 1500)
})
rs.on("end", () => {
console.log("end")
})
rs.on("error", () => {
console.error("error")
})
let fs = require('fs');
const option = {
highWaterMark: 3,
flags: 'w',
mode: 0o666
};
var fs=require("fs");
var rs = fs.createReadStream("./Koala.jpg");//默认64KB
var ws = fs.createWriteStream("./Copy.jpg");//默认16KB,写入速度小于读取速度
rs.on("data",function(data){
var flag = ws.write(data);
if (!flag){ //缓冲区已满
rs.pause();//停止触发data事件
}
});
ws.on("drain",function(){
rs.resume();//如果当前的缓冲区写入完毕,就重新触发data事件
});
rs.on("end",function(){
ws.end();//将剩下的数据全部写入,并且关闭写入的文件
})
const fs = require("fs");
const ev = require("events");
const util = require("util");
const NEW_LINE = 0x0A;
const RETURN = 0x0D;
// 3个操作系统的换行不一样,windows是回车+换行 \r\n 就是 0d0a mac 是 \r linux 是 \n
// fs.readFile("./2.txt", function (err, data) {
// console.log(data)
// })
function LineReader(path) {
ev.call(this)
this._reader = fs.createReadStream(path);
this.on("newListener", (type, listener) => {
if (type === 'newLine') {
let buffers = []
this._reader.on("readable", () => {
let ch;
while ( null !== (ch = this._reader.read(1)) ) {
if (ch[0] === NEW_LINE) {
this.emit("newLine", Buffer.from(buffers));
} else if (ch[0] === RETURN) {
this.emit("newLine", Buffer.from(buffers));
buffers = [];
let nextCh = this._reader.read(1);
if (nextCh[0] !== NEW_LINE) {
buffers.push(nextCh[0])
}
} else {
buffers.push(ch[0])
}
}
})
this._reader.on("end", () => {
this.emit("newLine", Buffer.from(buffers));
this.emit("end")
})
}
});
}
util.inherits(LineReader, ev)
let reader = new LineReader("./2.txt");
reader.on("newLine", function (data) {
console.log(data.toString())
})
const fs = require("fs");
const Event = require("events");
class WriteStream extends Event {
constructor(path, opts) {
super(path, opts)
this.path = path;
this.flag = opts.flag || 'w'
this.mode = opts.mode || 0o666
this.start = opts.start || 0
// 写入索引
this.pos = this.start;
this.encoding = opts.encoding || "utf8"
this.autoClose = opts.autoClose;
this.highWaterMark = opts.highWaterMark || 16 * 1024;
this.writing = false;
// 缓存区字节大小
this.length = 0;
// 缓冲区 源码是链表
this.buffers = []
this.open()
}
open() {
fs.open(this.path, this.flag, this.mode, (err, fd) => {
if (err) {
if (this.autoClose) {
this.destry()
}
this.emit("error")
}
this.fd = fd;
this.emit("open")
})
}
write(chunk, encoding = 'utf8', cb) {
chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding)
// 缓存区长度增加
let len = chunk.length;
this.length += len;
if (this.writing) {
this.buffers.push({ chunk, encoding, cb })
console.log(this.buffers)
} else {
this.writing = true;
this._write(chunk, encoding, () => this.clearBuffer())
}
return this.length < this.highWaterMark;
}
_write(chunk, encoding, cb) {
if (typeof this.fd !== 'number') {
return this.once("open", function () {
this._write(chunk, encoding, cb);
})
}
fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, bytesWritten) => {
if (err) {
if (this.autoClose) {
this.emit("error");
this.destry();
}
}
this.pos += bytesWritten;
// 写入多少字节,缓存区要减少多少字节
this.length -= bytesWritten
cb && cb()
})
}
destry() {
fs.close(this.fd, () => {
this.emit("close")
})
}
clearBuffer() {
let data = this.buffers.shift()
if (data) {
this._write(data.chunk, data.encoding, () => this.clearBuffer())
} else {
this.writing = false;
// 缓存区清空了
this.emit("drain")
}
}
}
let ws = new WriteStream("./21.txt", {
highWaterMark: 3,
autoClose: true
})
let n = 9;
ws.on("error", () => {
console.log("error")
})
function write() {
let flag = true;
while (flag && n > 0) {
flag = ws.write(n + '')
n--;
}
ws.once("drain", () => {
console.log("drain")
write()
})
}
write()
const fs = require("fs");
const Event = require("events");
class ReadStream extends Event {
constructor(path, opts) {
super(path, opts)
this.path = path;
this.highWaterMark = opts.highWaterMark || 64 * 1024;
this.flag = opts.flag || 'r';
this.mode = opts.mode || 0o666;
this.start = opts.start || 0;
this.end = opts.end;
this.autoClose = opts.autoClose || false;
this.pos = this.start;
this.encoding = opts.encoding;
this.flowing = null;
this.buffer = Buffer.alloc(10);
this.open();
this.on("newListener", (type, listener) => {
// 监听了data事件就切换到流动模式
if (type === 'data') {
this.flowing = true;
this.read();
}
})
}
read() {
if (typeof this.fd !== 'number') {
return this.once("open", this.read)
}
let readNum = this.end ? Math.min(this.highWaterMark, this.end - this.pos + 1) : this.highWaterMark;
// this.buffer 不是缓冲区
fs.read(this.fd, this.buffer, 0, readNum, this.pos, (err, bytesRead, buffer) => {
if (err) {
if (this.autoClose) {
this.destroy()
}
return this.emit("error")
}
if (bytesRead) {
this.pos += bytesRead;
let data = this.buffer.slice(0, bytesRead);
data = this.encoding ? data.toString(this.encoding) : data;
this.emit("data", data);
if (this.end && this.pos > this.end) {
return this.endFn()
} else {
if(this.flowing){
this.read()
}
}
}
});
}
endFn() {
this.emit("end");
this.destroy();
}
open() {
fs.open(this.path, this.flag, this.mode, (err, fd) => {
if (err) {
if (this.autoClose) {
this.destroy();
this.emit("error", err);
}
}
this.fd = fd;
this.emit("open");
})
}
pipe(ws) {
this.on("data", data => {
let flag = ws.write(data);
if (!flag) {
this.pause();
}
});
ws.on("drain", this.resume)
}
pause() {
this.flowing = false;
}
resume = () => {
this.flowing = true;
this.read();
}
destroy() {
fs.close(this.fd, () => {
this.emit("close")
})
}
}
// 在可读流创建会,自动进入暂停模式,并填充缓存区
class ReadStream extends Event {
constructor(path, opts) {
super(path, opts)
this.path = path;
this.highWaterMark = opts.highWaterMark || 64 * 1024;
this.flag = opts.flag || 'r';
this.mode = opts.mode || 0o666;
this.start = opts.start || 0;
this.end = opts.end;
this.autoClose = opts.autoClose || false;
this.pos = this.start;
this.encoding = opts.encoding;
this.flowing = null;
// 这只是一个临时容器
this.buffer = Buffer.alloc(this.highWaterMark);
// 缓存区
this.cache = [];
// 缓存区的数据长度
this.len = 0;
this.open();
this.on("newListener", (type, listener) => {
// 监听了data事件就切换到流动模式
if (type === 'data') {
this.flowing = true;
this.read();
}
})
}
open() {
fs.open(this.path, this.flag, this.mode, (err, fd) => {
if (err) {
if (this.autoClose) {
this.destroy();
this.emit("error", err);
}
}
this.fd = fd;
this.emit("open");
this.read()
})
}
read(n) {
let ret;
if (n > 0 && n < this.len) {
ret = Buffer.alloc(n);
let index = 0;
let bf;
while (null != (bf = this.cache.shift())) {
for (let i = 0; i < bf.length; i++) {
ret[index++] = bf[i]
if (index === n) {
bf = bf.slice(i)
this.len -= n;
this.cache.unshift(bf)
break;
}
}
}
}
// 数据为空,或者数据小于最高水位线,就要重新读取
if (this.len == 0 || this.len < this.highWaterMark) {
fs.read(this.fd, this.buffer, 0, this.highWaterMark, null, (err, bytesRead) => {
if (bytesRead) {
let data = this.buffer.slice(0, bytesRead)
this.cache.push(data);
this.len += data.length;
this.emit("readable")
} else {
this.emit("end")
}
})
}
return ret;
}
}