最近工作中积累了不少模块的使用办法,特此备忘啦,哇咔咔
TCP服务器
TCP服务器需要用到net模块,建立TCP服务器的场景还是很多的,很多硬件发送帧都需要TCP服务器来接收,学会这个还是很实用,我用的方法也不多,大致为:
- 创建服务器: createServer
- socket连接建立
- socket处理
- socket连接关闭
直接上代码
var net = require('net');
//这里需要注意,直接写回环地址,比如localhost,或者127.0.0.1,就只会监听本机发来的连接,
//写成0.0.0.0 就可以处理发送到本机的所有连接了
var HOST = '0.0.0.0';
var PORT = 3030;
//创建tcp服务器,注意末尾需要把host和port加上,表示监听本机3030端口
net.createServer(function(sock) {
// 输出我们获得的连接
console.log('CONNECTED: ' +
sock.remoteAddress + ':' + sock.remotePort);
// 这是socket实例数据处理的一个事件,收到的数据会进入这里,然后交给回调函数的第一个参数
sock.on('data', function(data) {
//你可以在这里处理这个数据,做点什么事吧
});
// 这是socket实例关闭连接的一个事件
sock.on('close', function(data) {
console.log('CLOSED: ' +
sock.remoteAddress + ' ' + sock.remotePort);
});
}).listen(PORT, HOST);//不要忘记这里
//加个信息提示一下
console.log('Server listening on ' + HOST +':'+ PORT);
HTTP服务就不说了,做网站的开不了HTTP服务器,还是先去看看框架吧
Request模拟报文发送
Request我现在多用于测试自己写的api,大致使用:
- get方法
- post方法
还是直接上代码
var request = require('request');
///这里就是设置一下参数了
var options = {
//url不必说了,发向那个就写那个,这里是我的一个例子
url: 'http://localhost:3000/commodityManage/purchaseAdd',
//报文头,这里就是按需填写了,我因为希望发送和接收都是json格式,所以这么写
//一般为了安全会在报文头加token,这里你也是可以模拟的,可以去浏览器抓取请求报文,能理解的更深一些
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
//这里就是填写数据了,我这里使用的是post方法,get的话是不用加的,json格式大家应该看的懂吧
form: {
'commodityList': [
{
'commodityName': '坦克杯',
'commodityId': '4',
'commodityPrice': 79999,
'commodityNumber': 1
},
{
'commodityName': '飞机杯',
'commodityId': '5',
'commodityPrice': 128,
'commodityNumber': 1
}
],
'purchasePrice': 79999,
'userId': '2'
}
};
//这里为了方便写了一个函数,大家也可以直接放在下面,但是不推荐这么做
//在这里做了一个检测,当request抓取到error时,会传进回调函数的第一个参数,而第二个参数是返回报文
//第三个就是返回的信息了。200是正常处理的状态,所以进行一个显示操作
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body);
console.log("info:", info);
}
}
//这里也要注意,post需要这么写,使用get时,直接写request(options, callback);
request.post(options, callback);
当然,这是给http服务器发送报文,tcp发送报文的方式在net模块中有,非常简单,不再赘述
Buffer处理
Buffer处理也是我跟硬件打交道需要掌握的,硬件一般走tcp来传输数据,传输到的数据一般都是流式数据
这次工作我主要接触的是16进制的buffer,所以我大致用到的方法:
- buffer转json
- json转字符串
这次处理也是比较闹心,比较硬件给的buffer不是那么听话,所以先用JSON.stringify()来统一格式,然后转成字符串数组,但是这么做出了一个新问题,转出来的是10进制的,而我需要多16进制的字符串进行解析,所以,就多一个10进制转16进制的步骤,代码如下
var dataPromise_1 = JSON.stringify(data);
var dataPromise_2 = JSON.parse(dataPromise_1);
var array = dataPromise_2.data;
var str = '';
for(var num = 0; num < array.length; num++) {
//这里因为10进制中,01变成了1,为了还原,所以有了这个步骤
if(array[num] < 16) {
str += '0' + array[num].toString(16);
} else {
str += array[num].toString(16);
}
}
也许有人说,为什么不用toString(),硬件传过来的buffer编码布吉岛方式呀,心里苦,只能傻傻的这么暴力解决了
一些关于时间处理的函数
这次也是大大刷新了我对时间函数的用法,js提供的date对象,真是太好用了,不需要其他的模块,已经非常强大了
看api手册就能获得大部分信息,平时用chrome的控制台也能补全函数,不怕忘记,说几个这次遇到的问题
- 时区对齐
服务器直接获取客户端的时间对象,可能会出现在原有基础上加上8小时的问题,这个情况出现的原因是,我们国家统一采用北京所在时区的时间,而我国幅员辽阔,横阔多个时区,避免时区带来的混乱,所以有了这8小时的误差,但是我们在开发中,很多时候也是使用北京时间,所以就得消除8小时误差带来的影响
date.setHours(date.getHours() + date.getTimezoneOffset() / 60);//这样来消除
- 获得当前星期是一年中的第几个星期
一年中的星期数是有限的,对他们一一编号,在做时间选择的时候,是非常方便的,方法也非常简单
function getWeek(date, callback) {
var time,week,checkDate = new Date(date);
checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
time = checkDate.getTime();
checkDate.setMonth(0);
checkDate.setDate(1);
week=Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
callback(week);
}
3.关于时间选择器
大家应该都知道datetimepicker,我们可以通过这个选取两个时间点,来获取这两个时间点之间的数据,但是这里需要注意一点,我们在时间选择器上选择了年月日形式的两个时间点,比如2016年4月1日到2016年4月3日,我们想获取的是1号,2号和3号的数据,但是时间选择器的终止时间是2016年4月3日 00:00:00,这样就只能获取1号和2号的数据了,所以需要将时间拨快一天,这里就用到setDate这个方法
date.setDate(date.getDate() + 1);
登录相关的模块补充
登录相关的模块在我的另外一篇文章中系统的讲过了
点击这里查看
这里做一个小小的补充,就是我用到的加密模块bcrypt-nodejs
我们一般在数据库保存的是密文密码,不是明文密码,密码学是一个专门的学问,有兴趣的可以深入参考一下,这里我只说用法
var bcrypt = require('bcrypt-nodejs');
var password = user.password;//用户传入的明文密码
var hash = bcrypt.hashSync(password);//保存这个就行了
获取url的参数
我现在一般使用两种方法来在url里添加参数,所以,后台获取参数也会有所不同
- 在url加入json字符串
这应该是很常见的方法,前端js可以在请求的url里加上json字符串,格式还可以自己定义,只要前后端保持一致就行,node在后台的解析也很方便,不用自己写正则表达式了
var url = require('url');
var token = (url.parse(req.url, true).query.token;//假设我传入的参数名是token
- 路由参数
使用过express的童鞋应该知道这个,在参数比较少的情况下,这个东西也是很好用的,它不需要额外的模块来解析
var express = require('express');
var router = express.Router();
//路径后面加上:,再跟上的就是参数名
router.get('/index/:id', function(req, res, next) {
//参数名保持一致,就能从params里取到参数的值了
var id = req.params.id;
});
生成唯一的短Id
有的时候,做唯一性标识,而且不希望太长的时候,这是个很方便的模块
大家可以算算,你的数据量有多大时,会出现重复
var shortid = require('shortid');
var appId = shortid.generate();
还有很多用法自行google
文件上传
由于我使用的是express,官方推荐的中间件就是multer,这个东西确实不错,你不用这个,直接在req里是取不到文件的
使用它,有几个点要注意
- form表单必须有enctype="multipart/form-data" ,而且提交方式为post
- 多文件上传,input必须带上multiple="multiple",否则默认只能传一个文件
- 后台对于单文件和多文件的处理是不同的,单文件的路径保存在file里,多文件保存在files里
var multer = require('multer');
var appStorage = multer.diskStorage({
destination: function(req, file, callback) {
//存放的位置
callback(null, 'public/images');
},
filename: function(req, file, callback) {
var appId = req.params.appId;
console.log('appId', appId);
var fileFormat = (file.originalname).split(".");
//这一步会将存放的文件重命名成你想要的名称
callback(null, file.fieldname + '-' + appId + '.' + fileFormat[fileFormat.length - 1]);
}
});
//单文件使用single,里面的参数必须和input里的name一致
var startUpload = multer({ storage: appStorage}).single('startImages');
//多文件使用array,里面的参数必须和input里的name一致
var carouselUpload = multer({ storage: appStorageArray}).array('carouselImages');
//单文件处理
startUpload(req, res, function (error) {
if (error) {
//错误处理
} else {
//注意是file
console.log(req.file.path);
}
});
//多文件处理
carouselUpload(req, res, function (error) {
if (error) {
//错误处理
} else {
//注意是files,这里保存的是一个数组
console.log(req.files[0].path);
}
});
异步编程
讲真,node自带并发太折磨人了,写一个for循环,竟然每个循环体都是同步进行的,一旦有数据相关,for循环都不能用,一般方法是写回调,但是回调太多就成了大括号陷阱了,所以还是要借助模块来帮忙,所以,我目前是采用两种方法解决异步编程
- 数量少写回调
- 数量多用async
简单说一下async我用的用法
var async = require('async');
var taskList = [task_1, task_2, task_3];
async.eachSeries(taskList, function(item, callback_async) {
//item里面有taskList的值,用它可以来取值
//做点什么吧,接下来的回调会进入下一个任务
callback_async(null, item);
}, function(err) {
//错误处理
console.log(err);
if(err) {
callback({ success: false, errorMessage: err});
} else {
callback({ success: true});
}
});
有的时候会发现async也比较麻烦,那么推荐另外一个库co
co配合yield(es6),可以达到异步的目的
const co = require('co');
function* task_1() {
// 你的函数
}
function* task_2() {
// 你的函数
}
function* task_3() {
// 你的函数
}
co(function *() {
yield task_1;
yield task_2;
yield task_3;
}).then().catch();
闭包
node本来就是js,说到js就得说说闭包呀,其实什么是闭包这个问题也是比较难理解的
阮一峰的网络日志中有这么一个解释
闭包就是能够读取其他函数内部变量的函数。
那么,为什么要闭包?
我们不可能把变量都设置为全局变量,在函数中的变量,我们也有取出来的需求,在java的类中,有get方法可以直接获取,而我们闭包所做的,也就类似于这个了。
//Java
public class init(){
private String username;
}
public String getUsername() {
return username;
}
//js
function init() {
var name = "Mozilla";
function displayName() {
alert(name);
}
displayName();
}
原型
说到原型还是得把Java拿出来做对比,Java的类继承模型非常典型,而Js的则是饱受非议的原型继承
Java的类继承不是重点,但是要理解原型最好还是参考一下Java,有差异才有比较
对于Js,我还是习惯使用栗子,在Js的array对象中,是没有最大值,最小值的方法的,我们可以通过原型来精简我们的代码
Array.prototype.max =
function(){
return Math.max.apply({},this)
}
Array.prototype.min = function(){
return Math.min.apply({},this)
}
[1,2,3].max()// => 3
[1,2,3].min()// => 1
这样数组就直接可以取最大值最小值了,这样相当于重写原型中的方法(虽然原本没有)
原型的用法还很多,我们看看如何把js的对象做的和java的差不多
方法一:
var init = function(username) {
this.username = username;
};
init.prototype = {
getName: function() {
return this.username;
},
setName: function(username) {
this.username = username;
}
};
var test = new init();
init.setName("fghpdf");
init.getName(); // => "fghpdf"
方法二:
var init = function(username) {
this.username = username;
};
init.prototype = {
getName = function() {
return this.username;
},
setName = function(username) {
this.username = username;
},
return {
getName: getName,
setName: setName
}
};
var test = new init();
init.setName("fghpdf");
init.getName(); // => "fghpdf"
(proto) 属性
该属性可以获取或设置一个对象的原型
- 创建一个以指定对象为原型的对象
var obj = {
__proto__: myProto,
foo: 123,
bar: "abc"
};
- 为内置类型添加子类型
var MyArrayProto = Object.create(Array.prototype);
//还可以写成var MyArrayProto = {__proto__:Array.prototype};
MyArrayProto.foo = function (...) { ... };
function createMyArray() {
var arr = Array.prototype.slice.call(arguments);
arr.__proto__ = MyArrayProto;
return arr;
}
var myarr = createMyArray(1,2,3); //myarr会有foo方法,也会有其他的数组方法
函数序列化
函数自带toString方法,可以把函数转成字符串
function a() {
console.log("aaaa")
};
a.toString(); // => "function a(){console.log("aaaa")}"