成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Node搭建一個(gè)DNS服務(wù)器

DevTTL / 2947人閱讀

摘要:文件用來(lái)路由不同的服務(wù)器的有三個(gè)功能所以包含有三個(gè)模塊兒,開頭就引入了三個(gè)模塊兒,通過請(qǐng)求的路徑名稱我們路由到不同的處理模塊兒。鏈接到一個(gè)真實(shí)的服務(wù)器進(jìn)行域名解析,且始終使用網(wǎng)絡(luò)進(jìn)行查詢。

Node搭建DNS服務(wù)器的過程

接下來(lái)請(qǐng)深呼吸一大片代碼正奔涌而來(lái),該項(xiàng)目托管在https://github.com/MaxMin-she... 請(qǐng)各位同仁大神view指導(dǎo)。
1、route文件用來(lái)路由不同的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服務(wù)器的有三個(gè)功能所以包含有三個(gè)模塊兒,開頭就引入了三個(gè)模塊兒,通過請(qǐng)求的url路徑名稱我們路由到不同的處理模塊兒。這個(gè)簡(jiǎn)易的DNS服務(wù)器總共有四個(gè)自定義的模塊:

utile: 自定義的錯(cuò)誤處理模塊兒

DNSserver:進(jìn)行DNS域名解析,獲取域名所對(duì)應(yīng)的IP地址

Staticfiles: 根據(jù)請(qǐng)求路徑加載靜態(tài)文件

Saveimg: 存儲(chǔ)圖片,返回一個(gè)自定的存儲(chǔ)路徑

接下來(lái),我們分別來(lái)介紹這幾個(gè)模塊兒的功能和作用:

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將錯(cuò)誤的結(jié)果打印出來(lái)

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是一個(gè)包含著請(qǐng)求基本信息的字符串,以‘http://user:[email protected]:8080/path?query=string#hash’為例,主要包含的屬性字段有:
1、protocal:‘http’,協(xié)議類型
2、slashes:true,表示protocal冒號(hào)后面跟著兩個(gè)ASCII 斜杠字符
3、auth:"user:pass",由username:user和password:pass組成
4、host:"host.com:8080",由hostname(域名或者ip地址)和port(端口號(hào))8080組成
5、path:‘/path?query=string’,路徑,是由pathname(路徑名稱:‘/path’)和search(查詢名稱:‘?query=string’)組成
6、query:‘query=string’,由搜索對(duì)象形成

url.parse():url模塊的parse方法是將上面所說(shuō)的這些屬性值序列化成鍵值對(duì)對(duì)像。

假設(shè)我們的請(qǐng)求是:‘http://localhost:3000/dns?hostname=www.google.com’

querystring.parse(str[, sep[, eq[, options]]]):query的屬性的值類似于"hostname=www.google.com"這樣的值,querystring(查詢字符串)模塊兒的作用是用來(lái)解析和格式化url查詢字符串,其中的parse方法是將這種形式的字符串序列化成{hostname:google.com}這樣的鍵值對(duì)集合。
str:需要分割的查詢字符串
sep:用于界定查詢字符串中鍵值對(duì)的符號(hào)
eq:用于界定查詢字符串中鍵與值的符號(hào)
options:用來(lái)定義解碼查詢字符串的函數(shù)和解析鍵的最大數(shù)量

dns.resolve4(str,function(err, ad){}):dns(域名服務(wù)器模塊兒),這個(gè)模塊包含兩種函數(shù):1、使用底層操作系統(tǒng)工具進(jìn)行域名解析,無(wú)需進(jìn)行網(wǎng)絡(luò)通訊。2、鏈接到一個(gè)真實(shí)的DNS服務(wù)器進(jìn)行域名解析,且始終使用網(wǎng)絡(luò)進(jìn)行查詢。resolve4()屬于第二種函數(shù)。它的作用是使用DNS協(xié)議解析IPV4地址主機(jī)名,回調(diào)函數(shù)中的第一個(gè)參數(shù)是出現(xiàn)的錯(cuò)誤,第二個(gè)參數(shù)是解析得到的ip地址,注意:這里返回的addresses是一個(gè)IPV4地址數(shù)組,但是res.end()的數(shù)據(jù)類型只能是string或者buffer,所以在響應(yīng)是需要回調(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文件中有兩個(gè)輸出,index模塊是用來(lái)處理沒有輸入文件名時(shí)的默認(rèn)值,loadfiels模塊則可以根據(jù)文件名返回靜態(tài)文件,兩個(gè)模塊兒都使用的同一函數(shù)readStaticFiles進(jìn)行文件的讀取操作。

path模塊兒:用來(lái)處理文件和目錄的路徑

path.join([...paths]):將給定的所有path片段使用平臺(tái)特定的鏈接符鏈接成規(guī)范化路徑。在這個(gè)項(xiàng)目中,由于所有靜態(tài)文件都放在該項(xiàng)目的static目錄下面。所以,請(qǐng)求路徑之前要加一個(gè)相對(duì)路徑"static",不然就會(huì)報(bào)路徑錯(cuò)誤的error。

fs.readFile(path[,options],callback):根據(jù)路徑異步讀取文件,回調(diào)函數(shù)中返回兩個(gè)參數(shù):第一個(gè):error是讀取文件過程中產(chǎn)生的錯(cuò)誤,第二個(gè):data是讀取文件的二進(jìn)制數(shù)據(jù)流,如果在option中未指定編碼方式,返回的則是一個(gè)原始的buffer。

Saveimg模塊兒

在這個(gè)模塊中我們將實(shí)現(xiàn)圖片上傳下載的功能。
首先在html中完成一個(gè)form表單:




  
  


    

"multipart/form-data"是post的一種數(shù)據(jù)提交方式,用于附件的上傳,表單中還有file類型的控件,用于上傳一張圖片:

接下來(lái),我們了解一下請(qǐng)求報(bào)文頭和報(bào)文體的格式和內(nèi)容:

請(qǐng)求報(bào)文頭 req.headers,如下圖所示:


在請(qǐng)求報(bào)文頭中可以找到這些信息,其中Content-Type中的boundary屬性很重要,因?yàn)楦郊臄?shù)據(jù)量比較大,所以一個(gè)附件需要多部分提交才能完成,而boundary就是每一部分內(nèi)容之間的分隔符;Content-Length是報(bào)文的長(zhǎng)度。
報(bào)文體如下所示:

------WebKitFormBoundaryKXd7iAk5VsWqoaAY
Content-Disposition: form-data; name="userfile1"; filename="2.jpg"
Content-Type: image/jpeg


------WebKitFormBoundaryKXd7iAk5VsWqoaAY--

因?yàn)閭鬏數(shù)臄?shù)據(jù)量是未知的,所以通過boundary處理報(bào)文體是至關(guān)重要的一步。
在了解完附件上傳的報(bào)文形式以后,接下來(lái)我們將一步步的來(lái)實(shí)現(xiàn)圖片上傳的所有功能:

exports.saveimg = function (req, res) {
    if (req.method.toLowerCase() === "get") {
        getHandle(req, res);
    } else if (req.method.toLowerCase() === "post") {
        postHandle(req, res);
    }
}

首先,我們通過請(qǐng)求的方式來(lái)進(jìn)行分支處理,上傳圖片的http請(qǐng)求方式必須是post,postHandle函數(shù)的具體實(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);

            //重新設(shè)置文件名稱
            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)綁定了用來(lái)監(jiān)聽數(shù)據(jù)流的事件,req.on("end",callback)監(jiān)聽數(shù)據(jù)傳輸完畢的事件,由此可見對(duì)傳輸來(lái)的數(shù)據(jù)進(jìn)行的一系列操作都應(yīng)該放在這個(gè)監(jiān)聽事件的回調(diào)函數(shù)當(dāng)中

body變量中存儲(chǔ)的是本次附件上傳中存儲(chǔ)的所有數(shù)據(jù),如下圖所示(我只截取了body變量中的一部分):

以下截圖是body的開頭部分:

以下截圖則是body的結(jié)束部分:

(1)段代碼的作用是從請(qǐng)求報(bào)文頭的content-type屬性值中截取boundary分割符的內(nèi)容

(2)段代碼的作用是提取出報(bào)文體中的鍵值對(duì),querystring模塊兒中的parse方法上文有提及,解析后的具體內(nèi)容如下圖所示:


這段代碼的目的是為了獲取到報(bào)文體中的Content-Disposition字段和Content-Type字段,從Content-Disposition字段中可以獲取到文件名稱和文件格式,代碼3,4,5則完成了這個(gè)功能。
從打印的返回的報(bào)文體來(lái)看,Content-Type以后的所有數(shù)據(jù)就是圖片的編碼,所以接下來(lái)的任務(wù)就是將這個(gè)編碼提取出來(lái)

(6)段代碼的作用是找到圖片編碼字符串開始的index

(7)段代碼的作用是找到圖片編碼字符串的結(jié)束index,由body的結(jié)尾截圖可以看出,結(jié)束部分是由‘--boundary--’的形式組成,所以最后減去的除了boundary的長(zhǎng)度還有兩個(gè)‘--’的長(zhǎng)度4。

(8)段代碼中的binaryData則是圖片的完整編碼

隨機(jī)生成文件名稱的函數(shù)randomImgString的實(shí)現(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:文件的存儲(chǔ)路徑 2、data:文件編碼 3、options編碼方式 4、callback:寫入文件成功后的回調(diào)函數(shù)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92898.html

相關(guān)文章

  • 搭建Kubernetes集群時(shí)DNS無(wú)法解析問題的處理過程

    摘要:?jiǎn)栴}描述在搭建集群過程中,安裝了插件后,運(yùn)行一個(gè)容器,發(fā)現(xiàn)容器內(nèi)無(wú)法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時(shí)間后也無(wú)法解析集群內(nèi)域名??偨Y(jié)通過對(duì)問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...

    snowLu 評(píng)論0 收藏0
  • 搭建Kubernetes集群時(shí)DNS無(wú)法解析問題的處理過程

    摘要:?jiǎn)栴}描述在搭建集群過程中,安裝了插件后,運(yùn)行一個(gè)容器,發(fā)現(xiàn)容器內(nèi)無(wú)法解析集群外域名,一開始可以解析集群內(nèi)域名,一段時(shí)間后也無(wú)法解析集群內(nèi)域名??偨Y(jié)通過對(duì)問題的探究,也理解了集群中解析的完整過程,如圖。 showImg(https://segmentfault.com/img/remote/1460000015639330); 問題描述 在搭建Kubernetes集群過程中,安裝了kub...

    Berwin 評(píng)論0 收藏0
  • 手動(dòng)搭建kubernetes集群

    摘要:手動(dòng)搭建集群探索系列的第三篇,主要記錄手動(dòng)搭建集群的過程,部署部署用作服務(wù)發(fā)現(xiàn)。配置的子網(wǎng)范圍不能和的一致。 手動(dòng)搭建kubernetes集群 探索kubernetes系列的第三篇,主要記錄手動(dòng)搭建k8s集群的過程,部署dashboard, 部署DNS用作服務(wù)發(fā)現(xiàn)。順便記錄一下k8s中的一些資源的概念。 配置環(huán)境 這個(gè)步驟可以參考《Flannel with Docker》文中的步驟,不...

    warnerwu 評(píng)論0 收藏0
  • Kubernetes v1.0特性解析

    摘要:?jiǎn)栴}是不是定義的一個(gè)的容器集群是只部署在同一個(gè)主機(jī)上楊樂到目前是,同一個(gè)里的是部署在同一臺(tái)主機(jī)的。問題這個(gè)圖里的是安裝在哪里的所有的客戶端以及會(huì)連接這個(gè)嘛楊樂可以任意地方,只要能訪問到集群,會(huì)作為的出口。 kubernetes1.0剛剛發(fā)布,開源社區(qū)400多位貢獻(xiàn)者一年的努力,多達(dá)14000多次的代碼提交,最終達(dá)到了之前預(yù)計(jì)的milestone, 并意味著這個(gè)開源容器編排系統(tǒng)可以正式在...

    HackerShell 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<