摘要:文件用來路由不同的服務器的有三個功能所以包含有三個模塊兒,開頭就引入了三個模塊兒,通過請求的路徑名稱我們路由到不同的處理模塊兒。鏈接到一個真實的服務器進行域名解析,且始終使用網(wǎng)絡進行查詢。
Node搭建DNS服務器的過程
接下來請深呼吸一大片代碼正奔涌而來,該項目托管在https://github.com/MaxMin-she... 請各位同仁大神view指導。
1、route文件用來路由不同的action
const DNSserver = require("./src/controller/DNSserver.js"); const Staticfiels = require("./src/controller/Staticfiels.js"); const SaveImg = require("./src/controller/Saveimg.js") exports.getRouter = function (req, res) { console.log(url.parse(req.url)); const pathname = url.parse(req.url).pathname; switch (pathname) { case "/dns": DNSserver.parse(req, res); break; case "": case "/": case "/index": Staticfiels.index(req, res); break; case "/post/img": SaveImg.saveimg (req, res); break; default: Staticfiels.loadfiels(req, res, pathname); } } module.exports = exports;
DNS服務器的有三個功能所以包含有三個模塊兒,開頭就引入了三個模塊兒,通過請求的url路徑名稱我們路由到不同的處理模塊兒。這個簡易的DNS服務器總共有四個自定義的模塊:
utile: 自定義的錯誤處理模塊兒
DNSserver:進行DNS域名解析,獲取域名所對應的IP地址
Staticfiles: 根據(jù)請求路徑加載靜態(tài)文件
Saveimg: 存儲圖片,返回一個自定的存儲路徑
接下來,我們分別來介紹這幾個模塊兒的功能和作用:
utile模塊兒/** * handdle error * @param err * @param msg */ exports.errorHandle = function(err, type){ const time = new Date(); console.log(`------------------------ time: ${time} err: ${err} type: ${type} ------------------------ `); } module.exports = exports;
該模塊兒的作用是通過傳入的err,和提示的msg將錯誤的結(jié)果打印出來
DNSserver模塊兒/** * DNS解析 */ const url = require("url"); const querystring = require("querystring"); const dns = require("dns"); const util = require("../utile/utile.js"); /** * @param req * @param res */ exports.parse = function(req, res){ const query_url = url.parse(req.url); const query = querystring.parse(query_url.query); dns.resolve4(query["hostname"], function(err, addresses){ if(err){ util.errorHandle(err, "DNS failed"); res.writeHead(400); res.end(); } else { res.writeHead(200); res.end(addresses.toString()); } }); } module.exports = exports;
req.url:req.url是一個包含著請求基本信息的字符串,以‘http://user:pass@host.com:8080/path?query=string#hash’為例,主要包含的屬性字段有:
1、protocal:‘http’,協(xié)議類型
2、slashes:true,表示protocal冒號后面跟著兩個ASCII 斜杠字符
3、auth:"user:pass",由username:user和password:pass組成
4、host:"host.com:8080",由hostname(域名或者ip地址)和port(端口號)8080組成
5、path:‘/path?query=string’,路徑,是由pathname(路徑名稱:‘/path’)和search(查詢名稱:‘?query=string’)組成
6、query:‘query=string’,由搜索對象形成
url.parse():url模塊的parse方法是將上面所說的這些屬性值序列化成鍵值對對像。
假設我們的請求是:‘http://localhost:3000/dns?hostname=www.google.com’
querystring.parse(str[, sep[, eq[, options]]]):query的屬性的值類似于"hostname=www.google.com"這樣的值,querystring(查詢字符串)模塊兒的作用是用來解析和格式化url查詢字符串,其中的parse方法是將這種形式的字符串序列化成{hostname:google.com}這樣的鍵值對集合。
str:需要分割的查詢字符串
sep:用于界定查詢字符串中鍵值對的符號
eq:用于界定查詢字符串中鍵與值的符號
options:用來定義解碼查詢字符串的函數(shù)和解析鍵的最大數(shù)量
dns.resolve4(str,function(err, ad){}):dns(域名服務器模塊兒),這個模塊包含兩種函數(shù):1、使用底層操作系統(tǒng)工具進行域名解析,無需進行網(wǎng)絡通訊。2、鏈接到一個真實的DNS服務器進行域名解析,且始終使用網(wǎng)絡進行查詢。resolve4()屬于第二種函數(shù)。它的作用是使用DNS協(xié)議解析IPV4地址主機名,回調(diào)函數(shù)中的第一個參數(shù)是出現(xiàn)的錯誤,第二個參數(shù)是解析得到的ip地址,注意:這里返回的addresses是一個IPV4地址數(shù)組,但是res.end()的數(shù)據(jù)類型只能是string或者buffer,所以在響應是需要回調(diào)toString方法,將數(shù)組轉(zhuǎn)化成字符串。
Staticfiles模塊兒/** * get static files */ const fs = require("fs"); const path = require("path"); const util = require("../utile/utile.js"); /** * read Fiels * @param req * @param res * @param pathname */ const readStaticFiles = function(req, res, filename){ fs.readFile(filename, function(err, data){ if(err){ util.errorHandle(err, "filed readFile"); res.writeHead(404); res.end("We Got A Problem: File Not Found"); } else { res.writeHead(200); res.end(data); } }) } /** * exports function of reading files */ exports.loadfiels = function(req, res, pathname){ const filename = path.join("E:static", pathname); console.log(filename); readStaticFiles(req, res, filename); } module.exports = exports; /** * exports function of getting default page */ exports.index = function(req, res){ const filename = path.join("static", "html/index.html"); readStaticFiles(req, res, filename); }
Staticfiles文件中有兩個輸出,index模塊是用來處理沒有輸入文件名時的默認值,loadfiels模塊則可以根據(jù)文件名返回靜態(tài)文件,兩個模塊兒都使用的同一函數(shù)readStaticFiles進行文件的讀取操作。
path模塊兒:用來處理文件和目錄的路徑
path.join([...paths]):將給定的所有path片段使用平臺特定的鏈接符鏈接成規(guī)范化路徑。在這個項目中,由于所有靜態(tài)文件都放在該項目的static目錄下面。所以,請求路徑之前要加一個相對路徑"static",不然就會報路徑錯誤的error。
fs.readFile(path[,options],callback):根據(jù)路徑異步讀取文件,回調(diào)函數(shù)中返回兩個參數(shù):第一個:error是讀取文件過程中產(chǎn)生的錯誤,第二個:data是讀取文件的二進制數(shù)據(jù)流,如果在option中未指定編碼方式,返回的則是一個原始的buffer。
Saveimg模塊兒在這個模塊中我們將實現(xiàn)圖片上傳下載的功能。
首先在html中完成一個form表單:
"multipart/form-data"是post的一種數(shù)據(jù)提交方式,用于附件的上傳,表單中還有file類型的控件,用于上傳一張圖片:
接下來,我們了解一下請求報文頭和報文體的格式和內(nèi)容:
請求報文頭 req.headers,如下圖所示:
在請求報文頭中可以找到這些信息,其中Content-Type中的boundary屬性很重要,因為附件的數(shù)據(jù)量比較大,所以一個附件需要多部分提交才能完成,而boundary就是每一部分內(nèi)容之間的分隔符;Content-Length是報文的長度。
報文體如下所示:
------WebKitFormBoundaryKXd7iAk5VsWqoaAY Content-Disposition: form-data; name="userfile1"; filename="2.jpg" Content-Type: image/jpeg ------WebKitFormBoundaryKXd7iAk5VsWqoaAY--
因為傳輸?shù)臄?shù)據(jù)量是未知的,所以通過boundary處理報文體是至關重要的一步。
在了解完附件上傳的報文形式以后,接下來我們將一步步的來實現(xiàn)圖片上傳的所有功能:
exports.saveimg = function (req, res) { if (req.method.toLowerCase() === "get") { getHandle(req, res); } else if (req.method.toLowerCase() === "post") { postHandle(req, res); } }
首先,我們通過請求的方式來進行分支處理,上傳圖片的http請求方式必須是post,postHandle函數(shù)的具體實現(xiàn)過程如下:
function postHandle(req, res) { req.setEncoding("binary"); let body = ""; let filename = ""; req.on("data", function (chunk) { body += chunk; }); req.on("end", function () { const boundary = req.headers["content-type"].split(";")[1].replace("boundary=", ""); (1) const file = querystring.parse(body, " ", ":"); (2) if (file["Content-Type"].indexOf("image") !== -1) { const fileAr = file["Content-Disposition"].split("; ")[2].replace("filename=", "").split(".");(3) let filename = fileAr[0];(4) const imageState = fileAr[1].substring(0, fileAr[1].length-1);(5) const entireData = body.toString(); const contentType = file["Content-Type"].substring(1); const upperBound = entireData.indexOf(contentType) + contentType.length;(6) const tarStr = entireData.substring(upperBound).trim(); const boundaryIndex = tarStr.length - boundary.length - 4; const binaryData = tarStr.substring(0, boundaryIndex); //重新設置文件名稱 filename = randomImgString(filename); fs.writeFile(path.join(__dirname, `../../img/${filename}.${imageState}`), binaryData, { encoding: "binary" }, (err) => { if (err) { utile.errorHandle(err, "failed write file"); } else { res.writeHead(200, { "Content-Type": "application/json" }); const data = JSON.stringify({ "url":`http://127.0.0.1:3000/${filename}.${imageState}` }) console.log(data); res.write(data); res.end(); } }); } }) }
req.on("data",callback)綁定了用來監(jiān)聽數(shù)據(jù)流的事件,req.on("end",callback)監(jiān)聽數(shù)據(jù)傳輸完畢的事件,由此可見對傳輸來的數(shù)據(jù)進行的一系列操作都應該放在這個監(jiān)聽事件的回調(diào)函數(shù)當中
body變量中存儲的是本次附件上傳中存儲的所有數(shù)據(jù),如下圖所示(我只截取了body變量中的一部分):
以下截圖是body的開頭部分:
以下截圖則是body的結(jié)束部分:
(1)段代碼的作用是從請求報文頭的content-type屬性值中截取boundary分割符的內(nèi)容
(2)段代碼的作用是提取出報文體中的鍵值對,querystring模塊兒中的parse方法上文有提及,解析后的具體內(nèi)容如下圖所示:
這段代碼的目的是為了獲取到報文體中的Content-Disposition字段和Content-Type字段,從Content-Disposition字段中可以獲取到文件名稱和文件格式,代碼3,4,5則完成了這個功能。
從打印的返回的報文體來看,Content-Type以后的所有數(shù)據(jù)就是圖片的編碼,所以接下來的任務就是將這個編碼提取出來
(6)段代碼的作用是找到圖片編碼字符串開始的index
(7)段代碼的作用是找到圖片編碼字符串的結(jié)束index,由body的結(jié)尾截圖可以看出,結(jié)束部分是由‘--boundary--’的形式組成,所以最后減去的除了boundary的長度還有兩個‘--’的長度4。
(8)段代碼中的binaryData則是圖片的完整編碼
隨機生成文件名稱的函數(shù)randomImgString的實現(xiàn)過程如下所示:
/** * option to generate randomString */ function randomImgString(filename){ let outString = new Date().toTimeString(); outString += filename.substring(0, filename.indexOf(".")); outString = hash.update(outString) .digest("hex").substring(0, 15); return outString; }
*fs.writeFile(file, data[, options], callback):1、file:文件的存儲路徑 2、data:文件編碼 3、options編碼方式 4、callback:寫入文件成功后的回調(diào)函數(shù)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/92898.html
摘要:問題描述在搭建集群過程中,安裝了插件后,運行一個容器,發(fā)現(xiàn)容器內(nèi)無法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時間后也無法解析集群內(nèi)域名??偨Y(jié)通過對問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...
摘要:問題描述在搭建集群過程中,安裝了插件后,運行一個容器,發(fā)現(xiàn)容器內(nèi)無法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時間后也無法解析集群內(nèi)域名??偨Y(jié)通過對問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...
摘要:手動搭建集群探索系列的第三篇,主要記錄手動搭建集群的過程,部署部署用作服務發(fā)現(xiàn)。配置的子網(wǎng)范圍不能和的一致。 手動搭建kubernetes集群 探索kubernetes系列的第三篇,主要記錄手動搭建k8s集群的過程,部署dashboard, 部署DNS用作服務發(fā)現(xiàn)。順便記錄一下k8s中的一些資源的概念。 配置環(huán)境 這個步驟可以參考《Flannel with Docker》文中的步驟,不...
摘要:問題是不是定義的一個的容器集群是只部署在同一個主機上楊樂到目前是,同一個里的是部署在同一臺主機的。問題這個圖里的是安裝在哪里的所有的客戶端以及會連接這個嘛楊樂可以任意地方,只要能訪問到集群,會作為的出口。 kubernetes1.0剛剛發(fā)布,開源社區(qū)400多位貢獻者一年的努力,多達14000多次的代碼提交,最終達到了之前預計的milestone, 并意味著這個開源容器編排系統(tǒng)可以正式在...
閱讀 2189·2021-11-18 10:02
閱讀 2917·2021-09-04 16:41
閱讀 1219·2019-08-30 15:55
閱讀 1474·2019-08-29 17:27
閱讀 1244·2019-08-29 17:12
閱讀 2620·2019-08-29 15:38
閱讀 2952·2019-08-29 13:02
閱讀 2890·2019-08-29 12:29