node.js服务器基础

node.js的事件循环

node.js是基于事件驱动的,通常在代码中注册想要等待的事件,设定好回调函数,当事件触发的时候就会调用回调函数。如果node.js没有要处理的事件了,那整个就结束了;事件里面可以继续插入事件,如果有事件是一直要继续下去的,那么node 也就不会退出了,每一次事件处理结束后等待下一个事件的发生

console.log("HelloWorld");// 计时器事件, 每隔一段事件触发一次, time的单位是毫秒
/*setInterval(function  () {console.log("get time doing");
}, 5 * 1000); // 每间隔5秒调用一次*/// 插入一个事件,让它多长时间以后执行一次
setTimeout(function() {console.log("set time out");
}, 3 * 1000);// process是node的一个全局模块
console.log(process.pid);
console.log(process.version);
console.log(process.platform);
console.log(process.title);
console.log(process.argv); // 在启动的时候,我们可以往程序里面传入参数,参数都是字符串var argc = process.argv.length;
if (argc >= 3) {console.log(process.argv[2]);
}console.log(process.execPath); // node所在的路径
console.log(process.env); // 获得系统的环境变量// 当我们的node在每次退出的时候,都会抛出一个exit这样一个事件,如果我们用户监听这个事件,那么
// 当有exit事件发生的时候,我们之前设置的回掉函数,将会倍调用;
// process.on来监听事件
process.on("exit", function() {console.log("now node exit!!!!");
});//  当我们发生了一个未知的异常的时候,我们调用这个回掉函数;
// node 停止处理当前这个事件,继续等待下一个事件的处理,不会整个退出,
// 服务器就不会随意的奔溃
// 可以把这个错误,保存起来,方便我们去查找
process.on("uncaughtException", function(err) {console.log("uncaughtException called ", err);
});
// 当我们的程序,如果运行的时候有异常,那么这个时候,我们可以通过捕获异常,
// 如果说我们没有捕获的异常,这个uncaughtException
// 如果发生uncaughtException,node是会退出的;
// 没有主动捕获,所以javascript解释器,他是不会继续执行的;
// current work director 当前的工作目录
// ./ ---> C:\Home\workspace\node_js
// process.chdir("C:\\Home\\workspace"); // 修改我们的工作目录,工作目录默认你的node是在哪个目录下启动的,就是那个目录为你的工作目录
console.log(process.cwd());// while处理时间,进入等待时间之前调用,完成后,去事件里面来等待新的事件发生;
process.nextTick(function() {console.log("nextTick");
});

TCP网络连接和数据交换

node.js一般用on来监听对应的事件

服务器

// 将net模块 引入进来
var net = require("net");// 创建一个net.Server用来监听,当连接进来的时候,就会调用我们的函数
// client_sock,就是我们的与客户端通讯建立连接配对的socket
// client_sock 就是与客户端通讯的net.Socket
var server = net.createServer(function(client_sock) { console.log("client comming", client_sock.remoteAddress, client_sock.remotePort);// 设置你接受的格式, // client_sock.setEncoding("utf8");// client_sock.setEncoding("hex"); // 转成二进制的文本编码// // 客户端断开连接的时候处理,用户断线离开了client_sock.on("close", function() {console.log("close socket");});// 接收到客户端的数据,调用这个函数// data 默认是Buffer对象,如果你强制设置为utf8,那么底层会先转换成utf8的字符串,传给你// hex 底层会把这个Buffer对象转成二进制字符串传给你// 如果你没有设置任何编码 <Buffer 48 65 6c 6c 6f 57 6f 72 6c 64 21>// utf8 --> HelloWorld!!!   hex--> "48656c6c6f576f726c6421"client_sock.on("data", function(data) {console.log(data);client_sock.write("goodbye!!!");client_sock.end(); // 正常关闭});client_sock.on("error", function(err) {console.log("error", err);});
});// 当我开始监听的时候就会调用这个回掉函数
server.on("listening", function() {console.log("start listening...");
});// 监听发生错误的时候调用
server.on("error", function() {console.log("listen error");
});server.on("close", function() {console.log("server stop listener");
});
/*
server.on("connection", function(client_sock) {console.log("client comming 22222");
});
*/
// 编写代码,指示这个server监听到哪个端口上面。
// 127.0.0.1: 6080
// node就会来监听我们的server,等待连接接入
server.listen({port: 6080,host: "127.0.0.1",exclusive: true,
});// 停止node对server的监听事件处理,那么node就没有其他的事件要处理,所以就退出了。
// server.unref(); // 取消node,对server的事件的监听;
// server.close(); // 主动的掉这个server.close才会触发这个net.Server的close事件

客户端

var net = require("net");// net.Socket,
var sock = net.connect({port: 6080,host: "127.0.0.1",
}, function() {console.log('connected to server!');
});// 连接成功调用的事件
sock.on("connect",function() {console.log("connect success");// 在这里我们就可以发送数据了sock.write("HelloWorld!", "utf8");// end 
});
// end// 有错误发生调用的事件
sock.on("error", function(e) {console.log("error", e);
});// socket关闭的事件
sock.on("close", function() {console.log("close");
});// 对方发送了关闭数据包过来的事件
sock.on("end", function() {console.log("end event");
});// 当有数据发生的时候,调用;
sock.on("data", function(data) {console.log(data);
});

内存管理Buffer模块与大小端

1:4个字节的数据,存到内存的4个字节;
小尾: LE高位的数据 --> 高地址字节地方; --> “高高低低”
大尾: BE高位的数据 --> 低地址字节地方;
2: 通常我们的计算机的CPU,小尾存储;

分配:
Buffer.alloc(); Buffer.allocUnsafe(); Buffer.allocUnsafeSlow
Buffer.from(array); Buffer.from(buf); Buffer.from(string);
获取Buffer的长度 buf.length;

读写:
1: Buffer读取字节 buf[index];
2:根据数据属于大尾小尾来决定使用BE/LE;
3: readFloatBE/readDoubleBE / readFloatLE/readDoubleLE;
4: 读/写整数: read/write
Int8/UInt
Int16BE/Int16LE/UInt16BE/UInt16LE
Int32BE/Int32LE/UInt32BE/UInt32LE
IntBE/IntLE/UIntBE/UIntLE
floatBE/floatLE/doublBE/doubleLE

常用方法:
1:Buffer.byteLength(str, encoding) 返回字符对象对应的二进制长度;
2: 交换swap16, swap32, swap64 大尾小尾数据变化;
3: bu.values(); 遍历buf里面的每个字节;
4: buf转字符串 buf.toString();
5: Buffer转json字符串 JSON.stringify(buf) buf.toJSON;
6: buf.fill,用特定的数据填充buffer

// (1)给定一个大小
// (2)会给这些内存一个初值,如果你没有指定,那么这个初值就是0;
var buf = Buffer.alloc(10, 0xff);
console.log(buf);// (1) 给定分配一个给定大小的Buffer的内存
// (2) 不会对这些内存区赋初值的,随机的数据,它原来是什么就是什么;
// Unsafe 指的是没有初始化的内存
buf = Buffer.allocUnsafe(10);
console.log(buf);// 不重Buffer缓冲区里面分配,直接从操作系统分配
// Slow指的是没有重缓冲池里面高效的分配
// Unsafe, 指的是内存,没有被初始化
buf = Buffer.allocUnsafeSlow(10);
console.log(buf);// 获得我们的bufer对象的长度
// Buffer一旦分配,大小再也不能改变。
console.log(buf.length);// 方便的创建方式,复制
// 创建一个Buffer对象,用来存放这个字符串的二进制
buf = Buffer.from("Helloworld!");
console.log(buf);buf = Buffer.from([123, 22, 24, 36, 47, -1]);
console.log(buf);// 重新创建一个Buffer,然后把原来Buffer的数据拷贝给新的Buffer
var buf2 = Buffer.from(buf);
console.log(buf2);// buf[index] index [0, len - 1];
console.log(buf[0], buf[1]);// 以大尾的形式存放 4个字节的整数
// 0x00 00 ff ff --->655535
// 00 00 ff ff
// start从哪里开始
buf.writeInt32BE(65535, 0);
console.log(buf);// ff ff 00 00
buf.writeInt32LE(65535, 0);
console.log(buf);// offset是从哪里开始的位置
var value = buf.readInt32LE(0);
console.log(value);buf.writeFloatLE(3.14, 0);
console.log(buf.readFloatLE(0));var len = Buffer.byteLength("HelloWorld");
console.log(len);len = Buffer.byteLength(buf2);
console.log(len);// 4个字节的Int为例 4 * 4 = 16;
// 0, 1, 2, 3,| 4, 5, 6, 7, |8, 9, 10, 11, |12, 13, 14, 15
buf = Buffer.alloc(4 * 4);
buf.writeInt32LE(65535, 0);
buf.writeInt32LE(65535, 4);
buf.writeInt32LE(65535, 8);
buf.writeInt32LE(65535, 12);
console.log(buf);
buf.swap32();
// 3, 2, 1, 0,| 7, 6, 5, 4, |11, 10, 9, 8, |15, 14, 13, 12
console.log(buf);console.log(buf.readInt32BE(0));
console.log(buf.readInt32BE(4));
console.log(buf.readInt32BE(8));
console.log(buf.readInt32BE(12));for(var v of buf.values()) {console.log(v);
}// 二进制 根据特定的编码来转字符串
console.log(buf.toString('utf8'));
// "0000ffff0000ffff0000ffff0000ffff"
console.log(buf.toString('hex'));console.log(buf.toJSON());buf.fill('A');
console.log(buf);
console.log(buf.toString('utf8'));buf.fill("hello");
console.log(buf);
console.log(buf.toString('utf8'));

npm的安装和管理

1:node.js生态里的第三方模块可以通过npm工具来安装和使用,方便大家开发;
2: npm 安装 node.js 模块:
npm install 模块名称 (本地安装)运行目录/node_modules
npm install -g 模块名称 (全局安装 安装到系统的node_modules下)

注意:安装之前,使用npm init命令初始化一个package.json文件,这是当前项目的依赖模块

1:require项目文件 .js代码.json文本, .node二进制必须要使用:绝对路径(/), 相对路径(./, …/)开头
2: 没有写后缀名和路径的require项目文件默认为第三方库,依次加载: .js, .json. node;
3: 如果没有以绝对路径开头或以相对路径开头的为加载模块:
(1)系统模块去查找是否能找到;
(2)当前项目文件夹搜索 ./node_modules;
(3) 上一级项目文件夹搜索 ./node_modules,直到全部搜索完成;

websocket的使用

1:websocket 是一种通讯协议,底层是TCP socket, 基于TCP,它加入了自己的协议用来传输数据;
2: websocket是h5为了上层方便的使用socket而实现的;
3: 发送数据带着长度信息,能避免粘包的问题;
4: 客户端/服务器像事件驱动一样的编写代码,不用考虑底层复杂的事件模型;

服务器

// 加载node上websocket模块 ws;
var ws = require("ws");// 启动基于websocket的服务器,监听我们的客户端接入进来。
var server = new ws.Server({host: "127.0.0.1",port: 6080,
});// 监听接入进来的客户端事件
function websocket_add_listener(client_sock) {// close事件client_sock.on("close", function() {console.log("client close");});// error事件client_sock.on("error", function(err) {console.log("client error", err);});// end // message 事件, data已经是根据websocket协议解码开来的原始数据;// websocket底层有数据包的封包协议,所以,绝对不会出现粘包的情况。// 每解一个数据包,就会触发一个message事件;// 不会出现粘包的情况,send一次,就会把send的数据独立封包。// 想我们如果是直接基于TCP,我们要自己实现类是于websocket封包协议;client_sock.on("message", function(data) {console.log(data);client_sock.send("Thank you!");});// end 
}// connection 事件, 有客户端接入进来;
function on_server_client_comming (client_sock) {console.log("client comming");websocket_add_listener(client_sock);
}server.on("connection", on_server_client_comming);// error事件,表示的我们监听错误;
function on_server_listen_error(err) {}
server.on("error", on_server_listen_error);// headers事件, 回给客户端的字符。
function on_server_headers(data) {// console.log(data);
}
server.on("headers", on_server_headers);

客户端

var ws = require("ws");
// url ws://127.0.0.1:6080
// 创建了一个客户端的socket,然后让这个客户端去连接服务器的socket
var sock = new ws("ws://127.0.0.1:6080");
sock.on("open", function () {console.log("connect success !!!!");sock.send("HelloWorld1");sock.send("HelloWorld2");sock.send("HelloWorld3");sock.send("HelloWorld4");sock.send(Buffer.alloc(10));
});sock.on("error", function(err) {console.log("error: ", err);
});sock.on("close", function() {console.log("close");
});sock.on("message", function(data) {console.log(data);
});

使用浏览器时:

<!DOCTYPE html>
<html>
<head><title>skynet WebSocket example</title>
</head>
<body>   <script>var ws = new WebSocket('ws://127.0.0.1:6080/ws');ws.onopen = function(){alert("open");ws.send('WebSocket'); };ws.onmessage = function(ev){alert(ev.data);};ws.onclose = function(ev){alert("close");};ws.onerror = function(ev){console.log(ev);alert("error");};</script>
</body>
</html>

TCP的拆包封包

1: 在通讯的过程中,我们可能有发送多个数据包,数据包A,数据包B,数据包C,此时我们最好的期望是每次收到数据包A,数据包B,数据包C。但是TCP底层为了传送性能,可能会一次把ABC所有数据一起传过来,这个时候收到的是A+B+C,这个时候上层傻眼了,无法区分A,B,C这个叫做—粘包;

处理方式:
1:要解决ABC数据包无法正确的拆分出A,B,C三个数据,我们需要在ABC之间插入长度/分解标志,这样根据长度和分解标志来解析出ABC的数据包;
2: 打入长度信息两种方式:(1)“数据长度” + 包体 (2)(包体 + 特定的结尾符号)
3: 本例采用 数据长度 +包体的方式。数据长度2个字节,超过2个字节大小的数据,上层可以分多次发送;
4: 这里的包,是上层的应用协议的包,与TCP的包是两回事;

工具脚本netpkg.js:

var netpkg = {// 根据封包协议我们读取包体的长度;read_pkg_size: function(pkg_data, offset) {if (offset > pkg_data.length - 2) { // 没有办法获取长度信息的;return -1; }var len = pkg_data.readUInt16LE(offset);return len;},// 把一个要发送的数据,封包 2个字节的长度 + 数据// data string 二进制的bufferpackage_data: function(data) {var buf = Buffer.allocUnsafe(2 + data.length);buf.writeInt16LE(2 + data.length, 0);buf.fill(data, 2);return buf;},unpack_cmd_from_data: function(client_sock, data) {var last_pkg = client_sock.last_pkg;var cmd_set = [];if (last_pkg != null) { // 上一次剩余没有处理完的半包;var buf = Buffer.concat([last_pkg, data], last_pkg.length + data.length);last_pkg = buf;}else {last_pkg = data;	}var offset = 0;var pkg_len = netpkg.read_pkg_size(last_pkg, offset);if (pkg_len < 0) {return cmd_set;}while(offset + pkg_len <= last_pkg.length) { // 判断是否有完整的包;// 根据长度信息来读取我们的数据,架设我们穿过来的是文本数据var cmd_buf = Buffer.allocUnsafe(pkg_len - 2); // 2个长度信息last_pkg.copy(cmd_buf, 0, offset + 2, offset + pkg_len);cmd_set.push(cmd_buf);offset += pkg_len;if (offset >= last_pkg.length) { // 正好我们的包处理完了;break;}pkg_len = netpkg.read_pkg_size(last_pkg, offset);if (pkg_len < 0) {break;}}// 能处理的数据包已经处理完成了,保存 0.几个包的数据if (offset >= last_pkg.length) {last_pkg = null;}else { // offset, length这段数据拷贝到新的Buffer里面var buf = Buffer.allocUnsafe(last_pkg.length - offset);last_pkg.copy(buf, 0, offset, last_pkg.length);last_pkg = buf;}// end client_sock.last_pkg = last_pkg;return cmd_set;}, // 模拟底层TCP 粘包的问题test_pkg_two_action: function(action1, action2) {var buf = Buffer.allocUnsafe(2 + 2 + action1.length + action2.length);buf.writeInt16LE(2 + action1.length, 0);buf.fill(action1, 2);var offset = 2 + action1.length;buf.writeInt16LE(2 + action2.length, offset);buf.fill(action2, offset + 2);return buf},// 模拟的一个大的数据包,分两次发送到客户端;// one cmd half_cmd + half_cmd2test_pkg_two_slice: function(half_cmd1, half_cmd2) {// var buf1 = Buffer.allocUnsafe(2 + half_cmd1.length);buf1.writeInt16LE(2 + half_cmd1.length + half_cmd2.length, 0);buf1.fill(half_cmd1, 2);var buf2 = Buffer.allocUnsafe(half_cmd2.length);buf2.fill(half_cmd2, 0);return [buf1, buf2];}
};module.exports = netpkg;

服务器脚本tcpserver.js:

var net = require("net");
var netpkg = require("./netpkg");
var protocol = require("./protocol");function process_one_cmd(sock, cmd) {console.log(cmd);var data = protocol.decode(cmd);console.log(data);
}var server = net.createServer(function(client_sock) { console.log("client comming", client_sock.remoteAddress, client_sock.remotePort);client_sock.last_pkg = null;client_sock.on("close", function() {console.log("close socket");});client_sock.on("data", function(data) {var cmd_set = netpkg.unpack_cmd_from_data(client_sock, data);for(var key in cmd_set) {process_one_cmd(client_sock, cmd_set[key]);}});client_sock.on("error", function(err) {console.log("error", err);});
});server.on("listening", function() {console.log("start listening...");
});server.on("error", function() {console.log("listen error");
});server.on("close", function() {console.log("server stop listener");
});server.listen({port: 6080,host: "127.0.0.1",exclusive: true,
});

二进制数据协议、Json数据协议

二进制协议:优点是数据包小传输效率高,缺点是要在代码中单独定义每个协议的封包解包,这个过程可以利用protobuf来进行。
Json协议:优点是可以用同一种方式进行封包解包,可读性强。缺点是包体较大,需要使用数字作为key来减小包体。
这里我们规定了传输的数据包含三个部分,0:服务号,1:命令号,2:数据,下面的代码体现了这两种协议的封包和解包。

var bin_encode = {0: {// 账号登陆的二进制协议打包, stype = 0, cmd_type = 1,// data.uname 存放数据名字, data.upwd = 密码 {uname: "xxxx", upwd = "xxxx };// 制定了我们的登陆命令协议,前个字节存放服务号,后两个字节我们存放命令号,后面我们存放uname字符串的长度,占2个字节;// 紧接着uname字符串的内容,接着是upwd的长度信息,占两个字节,紧接着是upwd的内容;1: function (data) { // encode_login_cmd // 前面2个字节来存放我们的服务号var cmd_len = 2 + 2 + 2 + data[0].length + 2 + data[1].length;var cmd_buf = Buffer.allocUnsafe(cmd_len);// 写入两个字节的服务号cmd_buf.writeUInt16LE(0, 0); // 写入了0号服务cmd_buf.writeUInt16LE(1, 2); // 紧跟两个字节,我们写这个命令的命令号// end // 数据体// unamevar len = data[0].length;cmd_buf.writeUInt16LE(len, 4); // 写入了0号服务cmd_buf.fill(data[0], 4 + 2);// upwdlen = data[1].length;var offset = 6 + data[0].length;cmd_buf.writeUInt16LE(len, offset); // 写入了0号服务cmd_buf.fill(data[1], offset + 2);// end return cmd_buf;},},};var bin_decode = {0: {// return 1: function(cmd_buf) {var cmd = {0: 0,1: 1,2: null,};var len = cmd_buf.readUInt16LE(4); // uname的长度var offset = 4 + 2;var data = {};data.uname = cmd_buf.toString("utf8", offset, len + offset);offset += len;len = cmd_buf.readUInt16LE(offset); // uname的长度offset += 2;data.upwd = cmd_buf.toString("utf8", offset, len + offset);		cmd[2] = data;	return cmd;},}
}function bin_encode_cmd(stype, cmd_type, data) {if (bin_encode[stype] && bin_encode[stype][cmd_type]) {return bin_encode[stype][cmd_type](data);}return null;
}function bin_decode_cmd(cmd_buf) {// 前面2个字节是服务号,接着两个字节是命令号var stype = cmd_buf.readUInt16LE(0);var cmd_type = cmd_buf.readUInt16LE(2);if (bin_decode[stype] && bin_decode[stype][cmd_type]) {return bin_decode[stype][cmd_type](cmd_buf);}return null;// end 
}var bin_protocol = {encode: bin_encode_cmd,decode: bin_decode_cmd,
}function json_encode(stype, cmd_type, data) {var data = {0: stype,1: cmd_type,2: data,};var cmd_buf = JSON.stringify(data);console.log(cmd_buf);return cmd_buf;
}function json_decode(cmd_buf) {var cmd_data = JSON.parse(cmd_buf);console.log(cmd_data);return cmd_data;
}var json_protocol = {encode: json_encode,decode: json_decode,
}
module.exports = bin_protocol;
// module.exports = json_protocol;

Http基础和Webserver

1: http server是一种支持http协议的服务器;
2: http server监听在一个端口上面,等待客户端的http连接;
3: 客户端,创建一个TCP socket 连接到服务器;
4: 客户端像服务器发送一个 http协议的请求数据包;
5: 服务器获得这个请求,然后返回一个http响应数据包;
6: 服务器关闭Tcp socket, 客户端也关闭TCP socket;
7:Connection:keep-alive 如果有再次访问这个服务器上的网页,会继续使用这一条已经建立的连接,Keep-Alive不会永久保持连接,它有一个保持时间,默认是马上关闭;
在这里插入图片描述

请求方法: GET, POST, HEAD, PUT, DELETE, OPTIONS,TRACE, CONNECT,使用GET和POST即可满足一般需求
Post相比于Get不同的地方在于可以携带Body数据,数据内容通过Content-type体现

var express = require("express");
var path = require("path");
// 工作目录/www_root,静态网页存放的根目录index.html是入口文件
var app = express();
app.use(express.static(path.join(process.cwd(), "www_root")));
app.listen(6080);// web url的响应函数,http://127.0.0.1:6080/login访问,动态网页
app.get("/login", function (request, respones) {console.log(request.query);// 底层会打包成http协议的回应,发送给客户端respones.send("SUCCESS!");
});

客户端Get和Post的数据传输

var http = require("http");
/*[100] = "Continue",[101] = "Switching Protocols",[200] = "OK",[201] = "Created",[202] = "Accepted",[203] = "Non-Authoritative Information",[204] = "No Content",[205] = "Reset Content",[206] = "Partial Content",[300] = "Multiple Choices",[301] = "Moved Permanently",[302] = "Found",[303] = "See Other",[304] = "Not Modified",[305] = "Use Proxy",[307] = "Temporary Redirect",[400] = "Bad Request",[401] = "Unauthorized",[402] = "Payment Required",[403] = "Forbidden",[404] = "Not Found",[405] = "Method Not Allowed",[406] = "Not Acceptable",[407] = "Proxy Authentication Required",[408] = "Request Time-out",[409] = "Conflict",[410] = "Gone",[411] = "Length Required",[412] = "Precondition Failed",[413] = "Request Entity Too Large",[414] = "Request-URI Too Large",[415] = "Unsupported Media Type",[416] = "Requested range not satisfiable",[417] = "Expectation Failed",[500] = "Internal Server Error",[501] = "Not Implemented",[502] = "Bad Gateway",[503] = "Service Unavailable",[504] = "Gateway Time-out",[505] = "HTTP Version not supported",
}
*/
/*
callback(is_success, data/erro)
*/
// get请求的参数,是带在URL的地址上面的
function http_get(ip, port, url, params, callback) {// step1,创建一个 http.ClientRequestvar options = {host: "127.0.0.1",port: port,path: url + "?" + params,method: "GET"};// 当有请求返回的时候,参数就会被传递为http.IncomingMessagevar req = http.request(options, function(incoming_msg) {console.log("respones status " + incoming_msg.statusCode);// 监听IncomingMessage的data事件,当收到服务器发过来的数据的时候,触发这个事件incoming_msg.on("data", function(data) {if (incoming_msg.statusCode === 200) {callback(true, data);}});});// 把这个请求发送出去req.end();
}/*
http_get("127.0.0.1", 6080, "/login", "uname=blake&upwd=123456", function(is_ok, data) {if (is_ok) {console.log(data.toString());}
});
*/// post可以带body数据传到服务器
function http_post(ip, port, url, params, body, callback) {// step1,创建一个 http.ClientRequestvar options = {host: "127.0.0.1",port: port,path: url + "?" + params,method: "POST",headers: {"Content-Type": "application/x-www-form-urlencoded","Content-Length": body.length}};var req = http.request(options, function(incoming_msg) {console.log("respones status " + incoming_msg.statusCode);// 监听IncomingMessage的data事件,当收到服务器发过来的数据的时候,触发这个事件incoming_msg.on("data", function(data) {if (incoming_msg.statusCode === 200) {callback(true, data);}});});// step2 写入body数据req.write(body);// 发送请求req.end();
}http_post("127.0.0.1", 6080, "/upload", "filename=my_file.txt", "Hello Htpp Post", function(is_ok, data) {if (is_ok) {console.log("upload_success", data.toString());	}
});

服务器处理Get和Post请求

var path = require("path");
var express = require("express");
var app = express();
// 配置我们的网站的根目录,www_root
app.use(express.static(path.join(process.cwd(), "www_root")));
app.listen(6080);// 设置我们的跨域访问
app.all('*', function(req, res, next) {res.header("Access-Control-Allow-Origin", "*");res.header("Access-Control-Allow-Headers", "X-Requested-With");res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");res.header("X-Powered-By",' 3.2.1')res.header("Content-Type", "application/json;charset=utf-8");next();
});
// end// get, 处理响应
app.get("/login", function (request, respones) {console.log("/login comming");	// 服务器收到请求后,获取客户端get操作参数console.log(request.query);// 服务器回信息给客户端respones.send("SUCCESS");
});app.post("/upload", function(request, respones) {console.log("/upload comming");// 获得url上传来的参数console.log(request.query);// 获得用户给我们发送过来的数据// 监听我们的data来获得request.on("data", function(data) {console.log(data.toString());respones.send("UPLOAD OK");	});
});

文件的异步读写

1: 导入fs模块: var fs = require(“fs”);
2: 创建一个目录:
异步:fs.mkdir(“路径”, end_func() {});
同步:fs.mkdirSync(“路径”);
3: fs.existsSync(path),异步的被废弃;
4: fs.rename() 修改文件的名字;
5: 打开一个文件: fs.open/fs.openSync
6: 关闭一个文件: fs.close/fs.closeSync;
7: fs.read/fs.readSync;
8: fs.readFile/fs.readFileSync 异步读文件;
9:fs.readdir/fs.readdirSync 读文件夹数据;
10: fs.write/fs.writeSync
11: fs.writeFile/fs.writeFileSync 写文件;

// 获得我们的fs模块
var fs = require("fs");// 等待我们创建好了这个文件夹以后,再来执行下一条指令
// fs.mkdirSync("./output");
// 当我们执行这个console.log的时候,文件夹已经创建好了,这个是同步
// console.log("hello");
/*
if (fs.existsSync("./output")) {console.log("output exist");fs.rename("./output", "./rename_output", function(err) {if (err) {console.log(err);return;}console.log("rename sucess");});
}
else {// 异步fs.mkdir("./output", function(err) {if (err) {console.log(err);return;}// 回掉函数的位置,能保证你已经穿件好这个好这个我文件夹。console.log("success");// end 	});	}// 获取文件信息
/*fs.stat("./hello.txt", function(err, stat) {if (err) {console.log(err);return;}console.log(stat.size);console.log(stat.ctime);
});*/
// end// 同步
var stat = fs.statSync("./hello.txt");
console.log(stat.size);
// end// 异步读一个文件
/*fs.readFile("./hello.txt", function(err, file_data) {if (err) {console.log(err);return;}console.log(file_data.toString());
});*/
// end// 同步读一个文件
var file_data = fs.readFileSync("./hello.txt");
console.log(file_data);
// end // 打开一个文件
fs.open("./hello.txt", "r", function(err, file_handle) {if (err) {console.log(err);return;}// 拿到了访问这个文件的钥匙;文档 里面的fd,文件句柄console.log("open file success");fs.fstat(file_handle, function(err, stat) {var buf = Buffer.allocUnsafe(stat.size);// 异步读写,把我呢见读入到bufferfs.read(file_handle, buf, 0, stat.size, null, function(err) {fs.close(file_handle);	console.log(buf.toString());});});
})
// end // 异步写文件
/*fs.writeFile("./writefile.txt", "Hello this write File called!", function(err) {if (err) {console.log("err");return;}console.log("success");
});*/
// end
/*fs.open("./writefile.txt", "a", function(err, fd) {fs.write(fd, "Hello this write File called!", function(err) {fs.close(fd);});
});
*/var fd = fs.openSync("./writefile.txt", "a");
fs.writeSync(fd, "Hello this write File called!");
fs.closeSync(fd);

BASE64、MD5、SHA1编码解码

1:Base64编码的作用:由于某些系统中只能使用ASCII字符。Base64就是用来将非ASCII字符的数据转换成ASCII字符的一种方法。
Buffer输出成Base64编码;
new Buffer(content).toString(“base64”);
Base64编码 解码
new Buffer(content, “base64”).toString();

2: MD5值:
任何数据,经过MD5加密后,会生成一串16字节的二进制对应的 'hex’字符串;
var md5 = crypto.createHash(‘md5’);
md5.update(content);
md5.digest(‘hex’);

3: SHA1:
任何数据,经过SHA1加密后,会生成一串20字节的二进制对应的 'hex’字符串
var sha1 = crypto.createHash(‘sha1’);
sha1.update(content);
md5.digest(‘hex’);

base64

var fs = require("fs");function base64_encode(content) {var buf = new Buffer(content);var base64 = buf.toString("base64");return base64;
}function base64_decode(base64_str) {var buf = new Buffer(base64_str, "base64");return buf;
}// 进一段二进制的数据转成 base64
var base64_str = base64_encode([0xff, 0xf1, 0x11, 0x13, 0xf7]);
console.log(base64_str);
// var decode_buf = base64_decode(base64_str);
console.log(decode_buf);// 二进制的图片,转成文本,传送, BASE64编码/解码;
fs.readFile("avator3.png", function(err, data) {if (err) {console.log(err);return;}var img_base64 = base64_encode(data);// console.log(img_base64);var img_data = base64_decode(img_base64);fs.writeFile("base64_img.png", img_data);
});

md5/sha1

hash算法不可逆的,不同内容HASH值不同,可以用来存放密码,判断两个文件是否相同等

var crypto = require("crypto");function md5(data) {var md5 = crypto.createHash("md5");md5.update(data);return md5.digest('hex'); 
}// b4 1c 16 1c 9b 8d 83 e3 42 01 bd 3f 57 b7 33 ca -->qidian
var str = md5("qidian");
console.log(str);// e10adc3949ba59abbe56e057f20f883e   --> 123456
var str2 = md5("123456");
console.log(str2);var data = fs.readFileSync("avator3.png");
// 84 92 a5 0b 16 95 c5 4d 43 62 12 69 5c f0 fc 52 16个字节的,--.Hex
str = md5(data);
console.log(str);// 4bde5246df25451af00ecba580bdb581
data = fs.readFileSync("base64_img.png");
str = md5(data)
console.log(str);
// end// d735871a64133ee062400659cf91b8234d1c1930
function sha1(data) {var sha1 = crypto.createHash("sha1");sha1.update(data);return sha1.digest('hex'); 
}str = sha1("HelloWorld!");
console.log(str);

定时器Timer

1: setImmediate: 事件循环的当前回合结束时要调用的函数;
2: setInterval 每隔一段时间调用一次;
3: setTimeout 一段时间后触发这个调用;
4:取消:clearImmediate/clearInterval/clearTimeout;对象都是有set系列的函数返回回来的;

fs.readFile("base64_img.png", function(err, data) {if (err) {return ;}console.log("begin setImmediate");// 等本次事件执行完成后,再来掉这里设置的回掉函数setImmediate(function() {console.log("yes setImmediate called");});console.log("end setImmediate");});setTimeout(function(data) {console.log(data);
}, 3000, 12345);var timer = setInterval(function(data) { console.log("setInterval called: ", data);
}, 3000, "Hello");setTimeout(function(data) {clearInterval(timer);
}, 10000, 12345);

mysql数据库操作

var mysql = require("mysql");// 创建一个连接池,我们每次要发送 sql语句给mysql server都通过这个连接池来获得连接对象;
// 连接对象的有效性就有这个池来负责管理;
// 避免mysql在一段时间后,没有数据通讯的情况下会自动关闭连接通道;var conn_pool = mysql.createPool({host: "127.0.0.1", // 数据库服务器的IP地址port: 3306, // my.cnf指定了端口,默认的mysql的端口是3306,database: "my_database", // 要连接的数据库user: "root",password: "123456",
});// callback 1: err, 2, rows, 3, 每个字段的说明
// 异步,执行完后腰等回掉,不是马上就有结果。
// 异步的mysql的操作能投,提升我们的服务器的吞吐量;function mysql_exec(sql, callback) {// getConnection 是从这个连接池里面获得mysql的连接通道,// 异步获取的,如果有结果了,就会调用一个回掉函数;// err, 是否有错误,如果没有错误err = null, 如果成功后面conn就是我们连接池// 返回給我们的和mysql server 进行通讯的句柄conn_pool.getConnection(function(err, conn) {if (err) { // 如果有错误信息if(callback) {callback(err, null, null);}return;}// 发送数据库的cmd到mysql server;// query向服务器发送sql语句的命令,有返回的话,就会调用我们的回掉函数;conn.query(sql, function(sql_err, sql_result, fields_desic) {if (sql_err) {if (callback) {callback(sql_err, null, null);}return;}// sql_result返回给我们的结果// fields_desic 每个字段的描述if (callback) {callback(null, sql_result, fields_desic);}});});
}// select的查询结果就是 [包住结果(1条)]
var sql_cmd = "select * from uinfo limit 1";
mysql_exec(sql_cmd, function(err, sql_result, fields_desic) {if (err) {return;}if (sql_result) {console.log(sql_result);	}
});// end
// update一般不关心结果,但是他会给我们回一个我们update的结果的情况。
sql_cmd = "update uinfo set upwd = \"777777777\"";
mysql_exec(sql_cmd, function(err, sql_result, fields_desic) {if (err) {return;}if (sql_result) {console.log(sql_result);	}
});// 插入一条记录
sql_cmd = "insert into uinfo (uname, upwd) values(\"blake\", \"98989898\")";
mysql_exec(sql_cmd, function(err, sql_result, fields_desic) {console.log("get result");if (err) {console.log(err);return;}if (sql_result) {console.log(sql_result);	}
});

redis数据库操作

1:内存数据库,同时也能够保存数据到磁盘;
2: 比其他的内存数据库有着更多的数据类型:
列表,集合,排序集合,哈希表等;
3:主从结构: 数据可以备份到从服务器;
4: Redis数据操作速度快;
5:所有的操作都是原子操作;

哈希表:
HMSET key 字段 value 字段 value …;
HGETALL key
HEXISTS key 字段
HGET key 字段
HKEYS key 返回所有的字段filed
HMGET key filed
列表:
LPUSH/RPUSH list_name v1
LRANGE list_name start stop
LPOP/RPOP list_name
有序集合: -->排行
ZADD key 权重 value
ZRANGE key start stop
ZRANGE key start stop WITHSCORES (显示权重)

var redis = require("redis");// 创建一个client连接到了我们的redis server
var client = redis.createClient({host: "127.0.0.1",port: 6379,db: 0,
});// 表
// key--->value
// set, get
client.set("my_redis_class_key", "123456");
// err, data
client.get("my_redis_class_key", function  (err, data) {if (err) {return;}console.log(data);
});
// end // 哈希表, 用户表
client.hmset("00015_redis_class_user", {uname: "blake",upwd: "123456",uemail: "1332222@qq.com",	
}, function(err) {});client.hgetall("00015_redis_class_user", function(err, data) {if (err) {return;}console.log(data);
});client.hget("00015_redis_class_user", "uname", function(err, data) {if (err) {return;}console.log(data);
});// end // 列表,数组
client.lpush("0000_1111_7777_data", "xiaohong");
client.rpush("0000_1111_7777_data", "xiaotian");
client.lpush("0000_1111_7777_data", "huangdong");
client.lpush("0000_1111_7777_data", "blake");client.lrange("0000_1111_7777_data", 0, 10, function(err, data) {if (err) {return;}console.log(data);
});client.lpop("0000_1111_7777_data", function(err, data) {if (err) {return;}console.log(data);
});
// end // 排行耪的有序集合
client.zadd("0000_1111_rank", 500, "blake");
client.zadd("0000_1111_rank", 400, "huangdong");
client.zadd("0000_1111_rank", 300, "xiaotian");
client.zadd("0000_1111_rank", 100, "xiaoming");client.zrange("0000_1111_rank", 0, 10, function(err, data) {if (err) {return;}console.log(data);
});
// end // 事件
// 监听我们的error事件, 当有错误的时候,不会终止我们的node.js程序
// 如果链接服务器不上,我们会触发自动重连
client.on("error", function(err) {console.log(err);
});client.on("end", function() {});
// end 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/445420.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为 静态路由和bfd 侦测的实验

实验要求 sw1 上业务地址192.168.1.1/24 SW3 业务地址192.168.2.1/24 正常情况下走主链路&#xff0c;不正常的情况下走备份链路 2 配置 这是基本地址配置 开启了bfd 本端地址为 10.1.1.1 对端地址是10.1.1.2 关键是discrimination 分辨参数 …

塔吊识别数据集 yolo格式 共5076张图片 已划分好训练验证 txt格式 yolo可直接使用

塔吊识别数据集 yolo格式 共5076张图片 已划分好训练验证 txt格式 yolo可直接使用。 类别&#xff1a;塔吊(Tower-crane) 一种 训练数据已划分&#xff0c;配置文件稍做路径改动即可训练。 训练集&#xff1a; 4724 &#xff08;正面3224 负面1500&#xff09; 验证集&#xf…

纠删码参数自适应匹配问题ECP-AMP实验方案(中)

6.方法设计 6.1.数据获取 为了收集不同的文件大小和纠删码参数对性能指标的影响&#xff0c;本文在Hadoop平台上进行了模拟实验。Hadoop是一种开源的分布式存储和计算框架&#xff0c;它可以支持不同类型的纠删码&#xff0c;并提供了一些应用程序接口和工具来测试和评估纠删…

Nginx基础详解5(nginx集群、四七层的负载均衡、Jmeter工具的使用、实验验证集群的性能与单节点的性能)

续Nginx基础详解4&#xff08;location模块、nginx跨域问题的解决、nginx防盗链的设计原理及应用、nginx模块化解剖&#xff09;-CSDN博客 目录 14.nginx集群&#xff08;前传&#xff09; 14.1如何理解单节点和集群的概念 14.2单节点和集群的比较 14.3Nginx中的负载均衡…

选GB28181还是RTSP?

好多开发者&#xff0c;搞不清楚什么时候用GB28181&#xff0c;什么时候用RTSP&#xff0c;本文就二者使用场景和区别&#xff0c;做个大概的探讨&#xff1a; 定义与性质&#xff1a; GB28181&#xff1a;是中国国家标准&#xff0c;全称为《公共安全视频监控联网系统信息传输…

AcWing 905:区间选点 ← 贪心算法

【题目来源】https://www.acwing.com/problem/content/907/【题目描述】 给定 N 个闭区间 [ai,bi]&#xff0c;请你在数轴上选择尽量少的点&#xff0c;使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。【输入格式】 第一行包…

Hopfield神经网络求解旅行商问题(Traveling Salesman Problem,TSP),提供完整MATLAB代码,复制粘贴即可运行

Hopfield神经网络是以美国物理学家约翰霍普菲尔德&#xff08;John Hopfield&#xff09;的名字命名的。他在1982年提出了这种类型的神经网络模型&#xff0c;因此通常被称为Hopfield网络。Hopfield网络是一种早期的人工神经网络&#xff0c;具有以下特点&#xff1a; 递归连接…

3、Docker搭建MQTT及Spring Boot 3.x集成MQTT

一、前言 本篇主要是围绕着两个点&#xff0c;1、Docker 搭建单机版本 MQTT&#xff08;EMQX&#xff09;&#xff0c;2、Spring Boot 3.x 集成 MQTT&#xff08;EMQX&#xff09;&#xff1b; 而且这里的 MQTT&#xff08;EMQX&#xff09;的搭建也只是一个简单的过程&#x…

linux 安装gitlab

安装环境 CentOS 7.7 (centos6.10会报错)2g内存防火墙关闭 安装步骤&#xff1a; 1 安装gitlab # yum install -y git curl policycoreutils-python openssh-server # 安装依赖 # wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.2.2-ce…

欧派家居被下调盈利预测:销售费用创新高,零售经销渠道压力不小

《港湾商业观察》王璐 在房地产等多重因素冲击之下&#xff0c;上半年不少家居上市公司交出的业绩答卷都不尽理想&#xff0c;这其中也包括了消费者所熟知的“家居一哥”欧派家居&#xff08;603833.SH&#xff09;。 从2023年下半年开始&#xff0c;胡歌的代言令全民对欧派家…

鸿蒙UI系统组件16——富文本编辑器(RichEditor)

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 RichEditor是支持图文混排和文本交互式编辑的组件&#xff0c;通常用于响应用户的对…

【C++入门篇 - 3】:从C到C++第二篇

文章目录 从C到C第二篇new和delete命名空间命名空间的访问 cin和coutstring的基本使用 从C到C第二篇 new和delete 在C中用来向系统申请堆区的内存空间 New的作用相当于C语言中的malloc Delete的作用相当于C语言中的free 注意&#xff1a;在C语言中&#xff0c;如果内存不够…

RISC-V笔记——语法依赖

1. 前言 Memory consistency model定义了使用Shared memory(共享内存)执行多线程(Multithread)程序所允许的行为规范。RISC-V使用的内存模型是RVWMO(RISC-V Weak Memory Ordering)&#xff0c;该模型旨在为架构师提供更高的灵活性&#xff0c;以构建高性能可拓展的设计&#x…

【C++栈 贪心 决策包容性】3170. 删除星号以后字典序最小的字符串|1772

本文涉及知道点 C栈 模拟 C贪心 LeetCode3170. 删除星号以后字典序最小的字符串 给你一个字符串 s 。它可能包含任意数量的 ‘’ 字符。你的任务是删除所有的 ’ 字符。 当字符串还存在至少一个 ‘’ 字符时&#xff0c;你可以执行以下操作&#xff1a; 删除最左边的 ’ 字符…

Go语言中的控制结构(四)

Go语言中的控制结构详解 控制结构是编程语言中控制代码执行流程的核心部分&#xff0c;Go语言通过if、for、switch等常见的控制结构&#xff0c;以及独有的defer、panic、recover机制&#xff0c;提供了强大且简洁的控制流管理。本文将详细讲解Go语言中的控制结构&#xff0c;包…

ASR-01和ESP32语音控制LED灯——基于VSCODE编辑器和ESP-IDF环境

一、ASR-01部分 大家不要问我软件哪里来&#xff0c;大家哪里买的的&#xff0c;就去哪里要&#xff0c;淘宝客服一定有&#xff0c;没有你就换一家。 图形化编程 原理&#xff1a;通过接收相匹配语音&#xff0c;赋值给ID&#xff0c;然后通过switch语句&#xff0c;判断ID值…

Linux内核USB3.0驱动框架分析--USB Hub代码分析

一、Linux 下USB Hub热插拔处理 1.1 Linux下USB HUB的驱动的实现和分析&#xff1a; USB设备是热插拔&#xff0c;因此在hub_probe函数中调用hub_configure函数来配置hub&#xff0c;在这个函数中主要是利用函数usb_alloc_urb函数来分配一个urb&#xff0c;利用usb_fill_int_u…

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…

【Linux】操作系统基础

1.冯诺依曼体系结构介绍 冯诺依曼体系结构如下&#xff1a; 在上图中「输⼊设备」和「输出设备」⼀般被称为计算机的外设&#xff0c;⽽「存储器」在冯 诺依曼体系结构中表示「内存」 输⼊设备⼀般包括&#xff1a;⽹卡、磁盘、键盘、触摸屏等 输出设备⼀般包括&#xff1a;…

java 自定义填充excel并导出

首先在resources下面放一个excel模板 1. 方法签名和请求映射 RequestMapping(value "/ExportXls") public ResponseEntity<byte[]> rwzcExportXls(HttpServletRequest request, RequestBody JSONArray jsonArray) throws IOException { RequestMapping(val…