最近工作中積累了不少模塊的使用辦法,特此備忘啦,哇咔咔
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")}"