小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

NodeJS創(chuàng)建HTTP、HTTPS服務(wù)器與客戶端

 西北望msm66g9f 2019-09-05

(給前端速報加星標(biāo),提升前端技能.)

作者: 奮飛

超文本傳輸協(xié)議(HTTP,HyperText Transfer Protocol)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。所有的WWW文件都必須遵守這個標(biāo)準(zhǔn)。設(shè)計HTTP最初的目的是為了提供一種發(fā)布和接收HTML頁面的方法。其屬于下圖七層網(wǎng)路協(xié)議的“應(yīng)用層”。

HTTP服務(wù)器
創(chuàng)建HTTP服務(wù)器
創(chuàng)建服務(wù)
方式一:回調(diào)方式
var server = http.createServer((request, response) => {// 接受客戶端請求時觸發(fā) ...});server.listen(10000, 'localhost', 511, () => {// 開始監(jiān)聽 ...});
方式二:事件監(jiān)聽方式
var server = http.createServer();// 接受客戶端請求時觸發(fā)server.on('request', (request, rsponse) => { ...});server.listen(10000, 'localhost', 511);// 開始監(jiān)聽server.on('listening', () => { ...});
注意:
  • server.listen(port, [host], [backlog], [callback])中的backlog參數(shù)為整數(shù),指定位于等待隊列中客戶端連接的最大數(shù)量,一旦超過這個長度,HTTP服務(wù)器將開始拒絕來自新客戶端的連接,默認(rèn)值為511。
  • 在HTTP請求服務(wù)器時,會發(fā)送兩次請求。一次是用戶發(fā)出請求,另一次是瀏覽器為頁面在收藏夾中的顯示圖標(biāo)(默認(rèn)為favicon.ico)而自動發(fā)出的請求。
關(guān)閉服務(wù)器
server.close();// 服務(wù)器關(guān)閉時會觸發(fā)close事件server.on('close', () => {...});
超時
server.setTimeout(60 * 1000, () => {console.log('超時了');});// 或者通過事件形式server.setTimeout(60 * 1000);server.on('timeout', () => {...});
注意:默認(rèn)超時時間為2分鐘
錯誤
server.on('error', (e) => {if(e.code === 'EADDRINUSE') {// 端口被占用 }});
獲取客戶端請求信息
當(dāng)從客戶端請求流中讀取到數(shù)據(jù)時會觸發(fā)data事件,當(dāng)讀取完客戶端請求流中的數(shù)據(jù)時觸發(fā)end事件。

Get請求
server.on('request', (request, response) => {if(request.url !== '/favicon.ico') {/* Get請求 */var params = url.parse(req.url, true).query;// 或者// var params = querystring.parse(url.parse(request.url).query);// 根據(jù)參數(shù)做處理// ...// 結(jié)束請求        response.end();    }  });
Post請求
server.on('request', (request, response) => { request.setEncoding('utf-8');if(request.url !== '/favicon.ico') {let result = ''; request.on('data', (data) => { result += data; }); request.on('end', () => {var params = JSON.parse(postData);console.log(`數(shù)據(jù)接收完畢:${result}`); });// 結(jié)束本次請求 response.end(); }// 結(jié)束本次請求 response.end(JSON.stringify({status: 'success'}));});
轉(zhuǎn)換URL字符串與查詢字符串
querystring模塊:轉(zhuǎn)換URL中的查詢字符串(URL中?之后,#之前)
querystring.stringify(obj, [sep], [eq])querystring.parse(str, [sep], [eq], [option])
sep:分割符,默認(rèn)&
eq:分配字符,默認(rèn)=
options:{maxKeys: number}指定轉(zhuǎn)換后對象中的屬性個數(shù)let str = querystring.stringify({name: 'ligang', age: 27});console.log(str); // name=ligang&age=27let obj = querystring.parse(str);console.log(obj); // { name: 'ligang', age: '27' }
url模塊:轉(zhuǎn)換完整URL字符串
url.parse(urlStr, [parseQueryString])
parseQueryString:如果為true,將查詢字符通過querystring轉(zhuǎn)換為對象;默認(rèn)false。
url.resolve(from, to);
將二者結(jié)合成一個路徑,from、to既可以是相對路徑也可以是絕對路徑。
// http:///javascript/a?a=1url.resolve('http:///javascript/', 'a?a=1'); // http:///a?a=1url.resolve('http:///javascript/', '/a?a=1');
注意:具體合并規(guī)則,請查看《Node權(quán)威指南》— 8.1HTTP服務(wù)器。

var urlStr = 'http:///javascript/?name=lg&uid=1#a/b/c';console.log(url.parse(urlStr, true));/*Url { protocol: 'http:', slashes: true, auth: null, host: '', port: null, hostname: '',hash: '#a/b/c', search: '?name=lg&uid=1', query: { name: 'lg', uid: '1' }, pathname: '/javascript/', path: '/javascript/?name=lg&uid=1', href: 'http:///javascript/?name=lg&uid=1#a/b/c'}*/
發(fā)送服務(wù)器端響應(yīng)流
response.writeHead(statusCode, [reasonPhrase], [headers]);// 或者response.setHeader(name, value);
響應(yīng)頭中包含的一些常用字段:

示例:
response.writeHead(200, {'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': 'http://localhost'});// 或者response.statusCode = 200;response.setHeader('Content-Type', 'text/plain');response.setHeader('Access-Control-Allow-Origin', 'http://localhost');
writeHead和setHeader區(qū)別:
writeHead:該方法被調(diào)用時發(fā)送響應(yīng)頭
setHeader:write方法第一次被調(diào)用時發(fā)送響應(yīng)頭
/* 獲取響應(yīng)頭中的某個字段值 */response.getHeader(name);/* 刪除一個響應(yīng)字段值 */response.removeHeader(name);/* 該屬性表示響應(yīng)頭是否已發(fā)送 */response.headersSent;/* 在響應(yīng)數(shù)據(jù)的尾部增加一個頭信息 */response.addTrailers(headers);
示例:
// 必須再響應(yīng)頭中添加Trailer字段,并且其值設(shè)置為追加的響應(yīng)頭中所指定的字段名response.write(200, {'Content-Type': 'text/plain', 'Trailer': 'Content-MD5'});response.write('....');response.addTrailers({'Content-MD5', '...'});response.end();
特別說明:
當(dāng)再快速網(wǎng)路且數(shù)據(jù)量很小的情況下,Node將數(shù)據(jù)直接發(fā)送到操作系統(tǒng)內(nèi)核緩存區(qū)中,然后從該內(nèi)核緩存區(qū)中取出數(shù)據(jù)發(fā)送給請求方;如果網(wǎng)速很慢或者數(shù)據(jù)量很大,Node通常會將數(shù)據(jù)緩存在內(nèi)存中,在對方可以接受數(shù)據(jù)的情況下將內(nèi)存中的數(shù)據(jù)通過操作系統(tǒng)內(nèi)核緩存區(qū)發(fā)送給請求方。
response.write返回true,說明直接寫到了操作系統(tǒng)內(nèi)核緩存區(qū)中;返回false,說明暫時緩存的內(nèi)存中。每次需要通過調(diào)用response.end()來結(jié)束響應(yīng)。
響應(yīng)超時會觸發(fā)timeout事件;response.end()方法調(diào)用之前,如果連接中斷,會觸發(fā)close事件。
/* 設(shè)置請求超時時間2分鐘 */response.setTimeout(2 * 60 * 1000, () => {console.error('請求超時!'); });// 或者response.setTimout(2 * 60 * 1000);response.on('timeout', () => {console.error('請求超時!');});/* 連接中斷 */response.on('close', () => {console.error('連接中斷!');});
/** * HTTP服務(wù)端 * Created by ligang on 17/5/28. */import http from 'http';var server = http.createServer();// 接受客戶端請求時觸發(fā)server.on('request', (request, response) => {if(request.url !== '/favicon.ico') { response.setTimeout(2 * 60 * 1000, () => {console.error('請求超時!'); }); response.on('close', () => {console.error('請求中斷!'); });let result = ''; request.on('data', (data) => { result += data; }); request.on('end', () => {console.log(`服務(wù)器數(shù)據(jù)接收完畢:${result}`); response.statusCode = 200; response.write('收到!'); response.end(); // 結(jié)束本次請求 }); }});server.listen(10000, 'localhost', 511);// 開始監(jiān)聽server.on('listening', () => {console.log('開始監(jiān)聽');});server.on('error', (e) => {if(e.code === 'EADDRINUSE') {console.log('端口被占用'); }else {console.log(`發(fā)生錯誤:${e.code}`); }});
HTTP客戶端
Node.js可以輕松向任何網(wǎng)站發(fā)送請求并讀取網(wǎng)站的響應(yīng)數(shù)據(jù)。
var req = http.request(options, callback);// get請求var req = http.get(options, callback);// 向目標(biāo)網(wǎng)站發(fā)送數(shù)據(jù)req.write(chunk, [encoding]);// 結(jié)束本次請求req.end([chucnk], [encoding]);// 中止本次請求req.abort();
其中,options用于指定目標(biāo)URL地址,如果該參數(shù)是一個字符串,將自動使用url模塊中的parse方法轉(zhuǎn)換為一個對象。注意:http.get()方法只能使用Get方式請求數(shù)據(jù),且無需調(diào)用req.end()方法,Node.js會自動調(diào)用。
/** * HTTP客戶端 * Created by ligang on 17/5/30. */import http from 'http';const options = { hostname: 'localhost', port: 10000, path: '/', method: 'post' },    req = http.request(options);req.write('你好,服務(wù)器');req.end();req.on('response', (res) => {console.log(`狀態(tài)碼:${res.statusCode}`);let result = ''; res.on('data', (data) => { result += data; }); res.on('end', () => {console.log(`客戶端接受到響應(yīng):${result}`); })});req.setTimeout(60* 1000, () => {console.log('超時了'); req.abort();});req.on('error', (error) => {if(error.code === 'ECONNERSET') {console.log('socket端口超時'); }else {console.log(`發(fā)送錯誤:${error.code}`); }});
代理服務(wù)器
/** * HTTP代理 * Created by ligang on 17/5/30. */import http from 'http';import url from 'url';/** * 服務(wù)端 */const server = http.createServer(async (req, res) => {// req.setEncoding('utf-8');/* 超時 2分鐘 */ res.setTimeout(2 * 60 * 1000, () => {// ... });/* 連接中斷 */ res.on('close', () => {// ...    });let options = {},        result = ''; options = await new Promise((resolve, reject) => {if(req.method === 'GET') { options = url.parse('http://localhost:10000' + req.url); resolve(options); }else if(req.method === 'POST') { req.on('data', (data) => { result += data;            }); req.on('end', () => { options = url.parse('http://localhost:10000' + req.url);// post請求必須制定 options.headers = {'content-type': 'application/json', }; resolve(options); }); } });    options.method = req.method;let content = await clientHttp(options, result ? JSON.parse(result) : result); res.setHeader('Content-Type', 'text/html'); res.write('<html><head><meta charset='UTF-8' /></head>') res.write(content); res.write('</html>');// 結(jié)束本次請求 res.end();});server.listen(10010, 'localhost', 511);/* 開始監(jiān)聽 */server.on('listening', () => {// ...});/* 監(jiān)聽錯誤 */server.on('error', (e) => {console.log(e.code);// ...});/** * 客戶端 * @param options 請求參數(shù) * @param data 請求數(shù)據(jù) */async function clientHttp(options, data) {let output = new Promise((resolve, reject) => {let req = http.request(options, (res) => {let result = ''; res.setEncoding('utf8'); res.on('data', function (chunk) { result += chunk; }); res.on('end', function () { resolve(result); }); }); req.setTimeout(60000, () => {console.error(`連接后臺超時 ${options.href}`); reject(); req.abort(); }); req.on('error', err => {console.error(`連接后臺報錯 ${err}`);if (err.code === 'ECONNRESET') {console.error(`socket超時 ${options.href}`); } else {console.error(`連接后臺報錯 ${err}`); } reject(); req.abort(); });// 存在請求數(shù)據(jù),發(fā)送請求數(shù)據(jù)if (data) { req.write(JSON.stringify(data)); } req.end(); });return await output;}
注意:
POST請求必須指定headers信息,否則會報錯socket hang up
獲取到options后需要重新指定其methodoptions.method = req.method;
HTTPS服務(wù)器
  • HTTPS使用https協(xié)議,默認(rèn)端口號44;
  • HTTPS需要向證書授證中心申請證書;
  • HTTPS服務(wù)器與客戶端之間傳輸是經(jīng)過SSL安全加密后的密文數(shù)據(jù);
創(chuàng)建公鑰、私鑰及證書
(1)創(chuàng)建私鑰
openssl genrsa -out privatekey.pem 1024
(2)創(chuàng)建證書簽名請求
openssl req -new -key privatekey.pem -out certrequest.csr
(3)獲取證書,線上證書需要經(jīng)過證書授證中心簽名的文件;下面只創(chuàng)建一個學(xué)習(xí)使用證書
openssl x509 -req -in certrequest.csr -signkey privatekey.pem -out certificate.pem
(4)創(chuàng)建pfx文件
openssl pkcs12 -export -in certificate.pem -inkey privatekey.pem -out certificate.pfx
HTTPS服務(wù)
創(chuàng)建HTTPS服務(wù)器同HTTP服務(wù)器大致相同,需要增加證書,創(chuàng)建HTTPS服務(wù)器時通過options參數(shù)設(shè)置。
import https from 'https';import fs from 'fs';var pk = fs.readFileSync('privatekey.pem'),    pc = fs.readFileSync('certificate.pem');var opts = {key: pk,cert: pc};var server = https.createServer(opts);
opts參數(shù)為一個對象,用于指定創(chuàng)建HTTPS服務(wù)器時配置的各種選項,下面只描述幾個必要選項:

HTTPS客戶端
const options = {hostname: 'localhost',port: 1443,path: '/',method: 'post',key: fs.readFileSync('privatekey.pem'),cert: fs.readFileSync('certificate.pem'),rejectUnhauthorized: false,agent: false // 從連接池中指定挑選一個當(dāng)前連接狀態(tài)為關(guān)閉的https.Agent    },    req = https.request(options);// 或者const options = {hostname: 'localhost',port: 1443,path: '/',method: 'post',key: fs.readFileSync('privatekey.pem'),cert: fs.readFileSync('certificate.pem'),rejectUnhauthorized: false,    };// 顯示指定https.Agent對象options.agent = new https.Agent(options);var req = https.request(options);
說明: 普通的 HTTPS 服務(wù)中,服務(wù)端不驗證客戶端的證書(但是需要攜帶證書),中間人可以作為客戶端與服務(wù)端成功完成 TLS 握手; 但是中間人沒有證書私鑰,無論如何也無法偽造成服務(wù)端跟客戶端建立 TLS 連接。
當(dāng)然如果你擁有證書私鑰,代理證書對應(yīng)的 HTTPS 網(wǎng)站當(dāng)然就沒問題了,所以這里的私鑰和公鑰只是格式書寫,沒有太大意義,只要將請求回來的數(shù)據(jù)原原本本交給瀏覽器來解析就算完成任務(wù)。 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多