node.js服务器基础

news2024/10/12 22:30:16

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 二进制的buffer
	package_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_cmd2
	test_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 

			// 数据体
			// uname
			var len = data[0].length;
			cmd_buf.writeUInt16LE(len, 4); // 写入了0号服务
			cmd_buf.fill(data[0], 4 + 2);

			// upwd
			len = 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.ClientRequest
	var options = {
		host: "127.0.0.1",
		port: port,
		path: url + "?" + params,
		method: "GET"
	};

	// 当有请求返回的时候,参数就会被传递为http.IncomingMessage
	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);
			}
		});
		 
	});

	// 把这个请求发送出去
	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.ClientRequest
	var 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);

		// 异步读写,把我呢见读入到buffer
		fs.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.coloradmin.cn/o/2208935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2021】知识图谱导论(陈华钧)——阅读思考与笔记

tips&#xff1a;其中所有【】表示的内容为博主本人想法&#xff0c;非作者观点&#xff0c;请注意辨别。 这是一本全面覆盖知识图谱多个方面的书籍。书中不仅详细介绍了知识图谱的表示、存储、获取、推理、融合、问答和分析等七大方面&#xff0c;还深入探讨了多模态知识图谱…

【Nginx系列】Nginx启动失败

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

[⑦5G NR]: PSS/SSS同步信号学习

在5G中&#xff0c;PSS(Primary Synchronization Signal) 主同步信号和SSS(Secondary Synchronization Signal)辅同步信号是用于物理层的信号&#xff0c;用于小区的搜索。 PSS 跟据协议38.211 7.4.2.2章节&#xff0c;PSS是3条长度为127的m序列&#xff0c;分别对应 N I D (…

空间解析几何4-空间中线段到圆的距离【附MATLAB代码】

目录 理论公式 matlab代码 理论公式 对于解一元4次方程&#xff0c;请详见我的博客 一元四次方程求解 -【附MATLAB代码】-CSDN博客文章浏览阅读1.4k次&#xff0c;点赞41次&#xff0c;收藏4次。最近在研究机器人的干涉&#xff08;碰撞&#xff09;检测&#xff0c;遇到了一…

义堂镇韦家巷村第十六届老人节暨孝善互助基金启动仪式成功举行

金秋十月爽&#xff0c;浓浓敬老情。10月11日晚&#xff0c;以“孝善韦家巷情暖重阳节”为主题的兰山区义堂镇韦家巷村第十六届老人节暨韦家巷村孝善互助基金启动仪式在韦家巷村文化广场盛大举行。 山东省民间文艺家协会副主席、临沂市民间文艺家协会主席、临沂市文联办公室主…

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史

使用git页面如何用旧项目创建一个新项目出来并且保留所有分支内容和提交历史 1、点击创建项目 2、点击导入项目

antd table合并复杂单元格、分组合并行、分组合并列、动态渲染列、嵌套表头

项目里遇到个需求&#xff0c;涉及到比较复杂的单元格合并 、嵌套表头、分组合并行、合并列等&#xff0c;并且数据列还是动态的&#xff0c;效果图如下&#xff1a; 可以分组设置【显示列】例如&#xff1a;当前组为【合同约定】&#xff0c;显示列为【合同节点】和【节点金额…

Milvus向量数据库管理工具[Attu]实践

Attu是一款专为Milvus向量数据库打造的开源数据库管理工具&#xff0c;提供了便捷的图形化界面&#xff0c;极大地简化了对Milvus数据库的操作与管理流程。阿里云Milvus集成了Attu&#xff0c;以便更加高效地管理数据库、集合&#xff08;Collection&#xff09;、索引&#xf…

protobufJavascrip编码解码演示

protobuf&Javascrip编码解码演示 start 写一下 protobuf 相关知识记录在 python 环境和 js 环境中如何处理 protobuf。 1. protobuf是什么&#xff1f; 1.1 介绍 Protocol Buffers(简称Protobuf) &#xff0c;是Google出品的序列化框架&#xff0c;与开发语言无关&…

【Go】GO语言知识总结浅析

Go语言是一种现代化的编程语言&#xff0c;由Google于2007年设计并于2009年发布。它旨在使编程变得简单、高效&#xff0c;并且可以在多核处理器上轻松构建高性能应用。Go语言的编程思想、发展历史、版本特点、运行原理、数据类型、应用场景&#xff0c;以及在web开发、网络编程…

C语言内存分配

概要 C语言的内存存储是一个复杂但非常重要的主题&#xff0c;涉及到静态内存、动态内存、指针、堆栈等多种机制&#xff0c;明白了c语言的内存分配&#xff0c;可以帮助我们更好的编程和学习c语言。 一.内存分配的类型 在C/C中内存分为5个区&#xff0c;分别为栈区、堆区、…

上海大爷惹恼韭菜不敢现身了

国庆节前股市暴涨&#xff0c;立即掀起了一股“全民炒股”热浪&#xff0c;但天不遂人愿&#xff0c;有大量韭菜散户很快就被套牢。 这与满嘴跑火车的上海大爷网红“爱在深秋”&#xff0c;曾鼓吹“A股会涨到上万点”多少有些关联。10月9日股市暴跌后&#xff0c;就不见他人影…

MySQL进阶 - SQL优化

01 插入数据 【一】insert优化&#xff1a; ① 不要一行一行插入数据&#xff0c;直接批量插入多行数据(但一个insert语句不要插入超过一千条)。 insert into 表名 (字段列表) values (xx,xx...),(xx,xx...)...&#xff1b; ② 在进行插入操作时手动提交事务&#xff0c;避免频…

【代码】集合set

哈喽大家好&#xff0c;我是学霸小羊&#xff0c;今天来讲一讲集合&#xff08;set&#xff09;。 在数学上&#xff0c;集合长这样&#xff1a; 那今天就来讲一讲编程上的集合。 集合的定义&#xff1a;把一些元素按照某些规律放在一起&#xff0c;就形成了一个集合。比如说…

PostgreSQL的学习心得和知识总结(一百五十四)|高级数据结构和算法之快速排序算法quicksort的实现及使用

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

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

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

豆包MarsCode 合伙人计划限时招募中,推广最高赢万元现金!

豆包MarsCode 合伙人计划正式上线啦&#xff01;作为官方推出的推广激励项目&#xff0c;豆包MarsCode 编程助手号召和鼓励所有用户向我们推荐新用户。 现在正式开启首轮合伙人招募&#xff0c;诚邀各位有意愿推广普及 AI 编程产品的伙伴成为我们的合伙人&#xff0c;全国限量…

【Linux】操作系统基础

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

用 logfire 提高应用的可观测性

Logfire是由 Pydantic 团队打造的平台, 还有供 app 使用的 library, 我们经常提到对应用要做 LMT(Log, Metrics, Trace), Logfire 可以用来收集、存储、分析和可视化日志数据和应用性能指标。通过集成日志和度量&#xff0c;Logfire 提供了一个统一的界面来管理应用程序和系统的…

时间序列预测(一)——线性回归(linear regression)

目录 一、原理与目的 1、线性回归基于两个的假设&#xff1a; 2、线性回归的优缺点: 3、线性回归的主要目的是&#xff1a; 二、损失函数&#xff08;loss function&#xff09; 1、平方误差损失函数&#xff08;忽略了噪声误差&#xff09; 2、均方误差损失函数 三、随…