2.HTTP學(xué)習(xí)

1.Server.js

const http = require("http");
const PORT = 8080;
const HOST = "localhost";
const WAIT_TIME =15;
//REF https://www.hacksparrow.com/express-js-https-server-client-example.html
//REF http://www.lxweimin.com/p/ab2741f78858
/*
 [A] HTTP module function
 ①http.METHODS
 ②http.STATUS_CODE
 ③http.createServer([requestListener]) //創(chuàng)建一個(gè)sever
    //return http.Server
 ④http.get(options[,callback)//設(shè)置method為get,且自動(dòng)調(diào)用req.end()
    //return http.clientRequest
 ⑤http.request(options[,callback])//創(chuàng)建一個(gè)client
    //return http.clientRequest 是可寫流
    var options = {
         hostname: 'www.google.com',
         port: 80,
         path: '/upload',
         method: 'POST',
         headers: {
         'Content-Type': 'application/x-www-form-urlencoded',
         'Content-Length': Buffer.byteLength(postData)
         }
         };
 [B] HTTP module class[http.ClientRequest | ]
 ①http.ClientRequest[Writable Stream interface + EventEmitter]
  -This object is created internally and returned from http.request().
  -add "responde" event on the request object
    //當(dāng)收到響應(yīng)頭部的時(shí)候?qū)?huì)觸發(fā)"response"事件,
    //響應(yīng)時(shí)間有個(gè)http.IncomingMessage參數(shù)
    //針對(duì)"response"事件,可以listen for "data" event
    //如果添加了"response"事件,必須消費(fèi)響應(yīng)對(duì)象的data,直到數(shù)據(jù)處理完成才會(huì)觸發(fā)"end"事件
  -"response" event[http.IncomingMessage] 當(dāng)請(qǐng)求的response被接收時(shí)觸發(fā)。 該事件只觸發(fā)一次。

 -req.write(chunk[,encoding][,callback]) //可以以Stream的形式多次發(fā)送body(可以設(shè)置header ['Transfer-Encoding', 'chunked'] )
 //return request
 -req.end([data][,encoding][,callback]) //相當(dāng)于調(diào)用wirte發(fā)送數(shù)據(jù)后再end

  -"abort" event 當(dāng)請(qǐng)求已被客戶端中止時(shí)觸發(fā)。 該事件僅在首次調(diào)用 abort() 時(shí)觸發(fā)。
  -"aborted" event 當(dāng)請(qǐng)求已被服務(wù)器中止且網(wǎng)絡(luò) socket 已關(guān)閉時(shí)觸發(fā)。
  -"connect" event 通過代理http訪問數(shù)據(jù)[response(http.IncomingMessage),socket(net.socket),head(Buffer)]每當(dāng)服務(wù)器響應(yīng)一個(gè)帶有 CONNECT 方法的請(qǐng)求時(shí)觸發(fā)。 如果該事件未被監(jiān)聽,則接收到 CONNECT 方法的客戶端會(huì)關(guān)閉它們的連接。
  -"continue" event 當(dāng)服務(wù)器發(fā)送了一個(gè) 100 Continue 的 HTTP 響應(yīng)時(shí)觸發(fā),通常因?yàn)樵撜?qǐng)求包含 Expect: 100-continue。 這是客戶端應(yīng)發(fā)送request body的指令

  -"socket" event[net.Socket] 當(dāng) socket 被分配給request后觸發(fā)。
  -"upgrade" event[response(http.IncomingMessage),socket(net.socket),head(Buffer)] 每當(dāng)服務(wù)器響應(yīng)一個(gè)upgrade請(qǐng)求時(shí)觸發(fā)。 如果該事件未被監(jiān)聽,則接收到升級(jí)請(qǐng)求頭的客戶端會(huì)關(guān)閉它們的連接。

  -req.abort()
  -req.setTimeout(timeout[,callback]) //設(shè)置request超時(shí)
  -req.setSocketKeepAlive([enable][,initDelay] //設(shè)置request保持長(zhǎng)連接
  -req.setNoDelay([noDelay]) //設(shè)置即可發(fā)送數(shù)據(jù),不緩存

 ②http.ClientResponse[Writable Stream interface[自己實(shí)現(xiàn)] + EventEmitter]
 ////作為http.Server()request事件的第二個(gè)參數(shù)
 -This object is created internally by an HTTP server--not by the user.
 -"close" event 表明在 response.end() 被調(diào)用或能夠刷新之前,底層連接被終止了。
 -"finish" event 當(dāng)響應(yīng)頭和主體的最后一部分已被交給操作系統(tǒng)通過網(wǎng)絡(luò)進(jìn)行傳輸時(shí),觸發(fā)該事件。 這并不意味著客戶端已接收到任何東西。
    //這個(gè)事件被觸發(fā)后,響應(yīng)對(duì)象上不再觸發(fā)其他任何事件
     res.addTrailers(headers) //消息發(fā)送后添加頭信息,Trailers will only be emitted if chunked encoding is used for the response
     res.writeHead(200, { 'Content-Type': 'text/plain','Trailer': 'Content-MD5' });
     res.write(fileData);
     res.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});
     res.end();

 -res.writeHead(statusCode[,statusMessage][,headers) //發(fā)送response header to the request.
     該方法在消息中只能被調(diào)用一次,且必須在 response.end() 被調(diào)用之前調(diào)用。
     如果在調(diào)用該方法之前調(diào)用 response.write() 或 response.end(),則隱式或可變的消息頭會(huì)被計(jì)算并調(diào)用該函數(shù)。
     當(dāng)消息頭已使用 response.setHeader() 設(shè)置,它們會(huì)被與其他消息頭合并傳給 response.writeHead(),帶消息頭的 response.writeHead() 有更高優(yōu)先級(jí)。
     var body = 'hello world';
     response.writeHead(200, {'Content-Length': Buffer.byteLength(body),
     'Content-Type': 'text/plain' });
 -res.write(chunk[,encoding][,callback]) This sends a chunk of the response body可以被多次調(diào)用
 -res.end([data][,encoding][,callback])  對(duì)于每個(gè)響應(yīng),response.end() 方法必須被調(diào)用。告訴服務(wù)器res被發(fā)送完成

 -res.finished //boolean代表res是否被發(fā)送完成
 -res.getHeader("name") //獲取指定name的頭內(nèi)容
    var contentType = response.getHeader('content-type');
 -res.headersSent //boolean如果消息頭被發(fā)送了就是true
 -res.removeHeader("name")//從隱式發(fā)送中移除一個(gè)頭
 -res.sendDate 當(dāng)為true時(shí)會(huì)自動(dòng)添加日期消息頭
 -res.setHeader("name","value")設(shè)置響應(yīng)的消息頭
    response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
 -res.setTimeout(msecs,callback) 設(shè)置socket的超時(shí)時(shí)間
 -res.statusCode 返回給客戶端的狀態(tài)碼
 -res.statusMessage 返回給客戶端的狀態(tài)信息

 ③http.IncomingMessage[可讀流接口,可由http.Server或http.ClientRequest創(chuàng)建,作為第一個(gè)參數(shù)傳給request或response事件]
 //作為http.Server()request事件的第一個(gè)參數(shù)
 -"data" event 請(qǐng)求體數(shù)據(jù)到來時(shí)被觸發(fā)。提供一個(gè)chunk參數(shù),表示接收的數(shù)據(jù)
 -"end" event 請(qǐng)求體數(shù)據(jù)傳送完畢時(shí)被觸發(fā),此后不會(huì)再有數(shù)據(jù)了
 -"close" event用戶請(qǐng)求結(jié)束時(shí)觸發(fā),用戶強(qiáng)制終止傳輸也是用close不是end事件,表明底層連接被關(guān)閉,每次響應(yīng)只發(fā)生一次

 -"aborted" event請(qǐng)求被客戶端終止且socket關(guān)閉時(shí)觸發(fā)

 -msg.headers
 -msg.httpVersion
 -msg.method
 -msg.url
 -msg.statusCode

 -msg.rawHeaders
 -msg.rawTrailers
 -msg.setTimeout(msecs,callback)
 -msg.statusMessage
 -msg.socket
 -msg.trailers

 ④http.Server[繼承net.Server,額外添加了一些事件]
 -"close" event 服務(wù)器關(guān)閉時(shí)觸發(fā)
 -"connection" event [socket]一個(gè)新的TCP流被創(chuàng)建時(shí)觸發(fā)
 -"request" event [req(msg),res(res)]每次接到一個(gè)請(qǐng)求時(shí)觸發(fā)

 -"connect" event[req(msg),socket(net.Scoket),head(Buffer)] //客戶端請(qǐng)求CONNECT方法是被觸發(fā)
 -"upgrade" event[req(msg),socket(net.Scoket),head(Buffer)] //客戶端請(qǐng)求UPGRADE時(shí)被觸發(fā)

 -server.close([callback]) //停止服務(wù)端接收服務(wù)
 -server.listen([port][,hostname][,backlog][,callback]) //綁定監(jiān)聽端口
 -server.listening //boolean 服務(wù)器是否在監(jiān)聽
 -server.maxHeadersCount //最大頭數(shù)量
 -server.setTimeout(msecs,calllback) //設(shè)置超時(shí),默認(rèn)是兩分鐘
 -server.timeout //默認(rèn)是兩分鐘



 */

//listener for "request" event
//req is http.IncomingMessage[readableStream]
//res is http.clientResponse[writableStream]
//return http.Server

const server = http.createServer((req,res)=>{

    //console.log 基礎(chǔ)信息
    console.log(`\nreq.headers:${JSON.stringify(req.headers)}`);
    console.log(`req.url:${req.url}`);
    //console.log(`req.statusCode:${req.statusCode}`);//valid for response
    console.log(`req.method:${req.method}`);
    console.log(`req.httpVersion:${req.httpVersion}`);

    let reqData = "";

    //msg有data,end,close事件
    req.on("data",(chunk)=>{
        reqData +=chunk;
    });

    req.on("end",()=>{
        console.log(`${new Date().getTime()} The request sends data completed`);
        console.log(`${new Date().getTime()} The reqData:${reqData}`);

        //準(zhǔn)備回傳數(shù)據(jù)給client
        let resBody= {
            "name":"www.baidu.com",
            "age":25,
            "scholl":"Sichuan University",
            "city":"America",
            "title":"Senior SoftEngineer",
            "tax":"090-33546"
        };

        //設(shè)置響應(yīng)頭,優(yōu)先級(jí)高于writeHead
        res.writeHead(200,{
            "Content-Type":"application/json",
            "Set-Cookie":["account=ryan","password=A123"],
            "Conten-Length":Buffer.byteLength(JSON.stringify(resBody))
        });

        //回傳數(shù)據(jù)
        res.write(JSON.stringify(resBody));
       // res.write(JSON.stringify(resBody));

        //每個(gè)響應(yīng)必須調(diào)用res.end()
        console.log(`${new Date().getTime()} Prepared to exec res.end()`);
        //如果這句話被屏蔽,15s后將會(huì)自動(dòng)觸發(fā)req監(jiān)聽的close事件
        res.end();

    });

    req.on("close",()=>{
        console.log(`${new Date().getTime()} IncomingMessage close`);
    });
});

//listener for "error"
server.on("error",(err)=>{
    if(err.code =="EADDRINUSE"){
        console.log(`Errors:${err.message}`);
        setTimeout(()=>{
            //觸發(fā)server的close事件,但是直到所有連接結(jié)束,才會(huì)關(guān)閉服務(wù)。
            server.close();
            // grab a random port
            server.listen(()=>{
                console.log("opened server on ",server.address());
            });
        })
    }else{
        console.log(err.message);
    }
},10000);

//listener for "listening"
server.listen(PORT,HOST,()=>{
    //查看服務(wù)器是否在監(jiān)聽
    console.log(`Server.listening:${server.listening}`);
    //查看服務(wù)器的監(jiān)聽port,host,family
    console.log("Opened server on ",server.address());
    //設(shè)置服務(wù)器的最大連接數(shù)
    server.maxConnections = 4;
    //設(shè)置服務(wù)器的最大header數(shù)量
    server.maxHeadersCount = 10;
    //查看最大頭數(shù)量
    console.log(`Server.maxHeadersCount:${server.maxHeadersCount}`);
    //設(shè)置timeout時(shí)間
    server.timeout = 1000*WAIT_TIME;
});

//listener for "close"
server.on("close",()=>{
    console.log("Server has closed.");
})

2.Client.js

const http = require("http");
const querystring = require("querystring");
const WAIT_TIME =2;
//將一個(gè)對(duì)象序列化為查詢字符串"msg=Hello Server From Http Client"
var postData1 = querystring.stringify({
    "msg":"Hello Server From Http Client"
});

//請(qǐng)求中的第一個(gè)參數(shù)
var options = {
    hostname:"localhost",
    port:8080,
    path:"/",
    method:"POST",
    headers:{
        "content-Type":"application/json",
        "content-Length":Buffer.byteLength(postData1)
    }
};

//http.request() returns an instance of the http.ClientRequest[Writable]
//listener for the 'response' event as a one time
//callback arguments http.IncomingMessage
const req =http.request(options,(res)=>{
    console.log(`STATUS:${res.statusCode}`);
    console.log(`HEADERS:${JSON.stringify(res.headers)}`);
    var resData="";

    //msg有data,end,close事件
    res.on("data",(chunk)=>{
        resData +=chunk;
    });

    res.on("end",()=>{
        console.log("The request receive data completed");
        console.log(`${new Date().getTime()} The resData:${resData}`);
    });

    res.on("close",()=>{
        console.log(`${new Date().getTime()} IncomingMessage close`);
    });

});

//write data to request body
//The ClientRequest instance is a writable stream.
// If one needs to upload a file with a POST request,
// then write to the ClientRequest object.
req.write(postData1);
req.end();

//設(shè)置響應(yīng)超時(shí)時(shí)間
req.setTimeout(1000*WAIT_TIME,()=>{
    console.log(`${new Date().getTime()} Server beyond ${WAIT_TIME}s to return data`);
});

//監(jiān)聽請(qǐng)求的錯(cuò)誤
req.on("error",(err)=>{
    console.log(`Errors:${err.message}`);
});

3.正常的執(zhí)行結(jié)果

[Server.js]
Server.listening:true
Opened server on  { address: '127.0.0.1', family: 'IPv4', port: 8080 }
Server.maxHeadersCount:10

req.headers:{"content-type":"application/json","content-length":"41","host":"localhost:8080","connection":"close"}
req.url:/
req.method:POST
req.httpVersion:1.1
1489919108333 The request sends data completed
1489919108334 The reqData:msg=Hello%20Server%20From%20Http%20Client
1489919108339 Prepared to exec res.end()

[Client.js]
STATUS:200
HEADERS:{"content-type":"application/json","set-cookie":["account=ryan","password=A123"],"conten-length":"128","date":"Sun, 19 Mar 2017 10:25:08 GMT","connection":"close","transfer-encoding":"chunked"}
The request receive data completed
1489919108349 The resData:{"name":"www.baidu.com","age":25,"scholl":"Sichuan University","city":"America","title":"Senior SoftEngineer","tax":"090-33546"}

4.斷點(diǎn)在Server.js的177行res.end(),觀察Client.js中是否觸發(fā)超時(shí)

[Server.js]
Server.listening:true
Opened server on  { address: '127.0.0.1', family: 'IPv4', port: 8080 }
Server.maxHeadersCount:10

req.headers:{"content-type":"application/json","content-length":"41","host":"localhost:8080","connection":"close"}
req.url:/
req.method:POST
req.httpVersion:1.1
1489918606073 The request sends data completed
1489918606073 The reqData:msg=Hello%20Server%20From%20Http%20Client
1489918606079 Prepared to exec res.end()

[Client.js]
STATUS:200
HEADERS:{"content-type":"application/json","set-cookie":["account=ryan","password=A123"],"conten-length":"128","date":"Sun, 19 Mar 2017 10:23:49 GMT","connection":"close","transfer-encoding":"chunked"}
1489919031123 Server beyond 2s to return data
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容