1. 程式人生 > >【node】node的核心模塊---http模塊,http的服務器和客戶端

【node】node的核心模塊---http模塊,http的服務器和客戶端

tel lis and 是否 使用 啟動 進入 path 入隊

http服務器和客戶端

node.js標準庫提供了http模塊,其中封裝了一個高效的http服務器和一個簡易的http客戶端,http.Server是一個基於事件的HTTP服務器,他的核心由Node.js下層的C++部分實現,而接口由JavaScript封裝,兼顧了性能和簡易性,http.request則是一個http客戶端工具,用於向http服務發起請求;

創建HTTP服務器

1. http.createServer()

//服務器實例
var httpServer = require("http")
httpServer.createServer(function(req,res){
        res.writeHead(200,{"Content-Type":"text/html"});
         res.write("<h2>hello world</h2>");
        res.end("<h3>你好</h3>");
}).listen(2333);
console.log("http server is listening at port 2333,localhost:2333");

http.createServer創建了一個http.server實例,將一個函數作為HTTP請求處理函數,這個函數接受兩個參數,分別是請求對象(req)和響應對象(res),在函數體內,res顯式的寫回了響應代碼200(表示請求成功),指定響應頭為"Content-Type":"text/html",然後寫入響應體通過res.end()結束並發送,最後調用listen函數,啟動服務器並且監聽2333端口

2. http.Server類

http.server是一個基於事件的http服務器,所有的請求都被封裝為獨立的事件,開發者只需要對他的事件編寫響應函數即可實現HTTP服務器的所有功能,它繼承自EventEmitter

  • connection: 當TCP鏈接建立時,該事件被觸發,提供一個參數socket,為net.Socket的實例,connection事件的粒度要大於request,因為客戶端在keep-Alive模式下可能在同一個鏈接內發送多次請求
wsServer.on(‘connection‘,function(sock){
 sock.on(‘a‘,function(num1,num2){
   console.log(`接到了瀏覽器發送的數據:${num1}`)
 })
 setInterval(function(){
   sock.emit(‘ttt‘,Math.random())
 },500)

})
  • close: 當服務器關閉時,該事件被觸發,註意不是在用戶連接斷開時,等等事件
  • request: 每次接收到一個請求時觸發。

var server = new http.Server(); 
server.on("request", function (req, res) {
   res.writeHead(200, { "Content-Type": "text/html" }); 
   res.write("<h2>node.js</h2>");
   res.end("<h3>test</h3>");
}).listen(2333); 
console.log("http server is listening at port 2333:2333");

3. http.ServerResponse

http.ServerResponse是返回給客戶端的信息,決定了用戶最終能看到的結果,它也是由http.Server的request事件發送的,作為第二個參數傳遞,一般簡稱response或者res。

http.ServerResponse有三個重要的成員函數,用於返回響應頭,響應內容以及結束請求:

(1)response.writeHead(statsCode,[headers]):向請求的客戶端發送響應頭,statusCode是HTTP狀態碼如200(請求成功),404(未找到)等,headers是一個類似關聯數組的對象,表示響應頭的每個屬性,該函數在請求內最多只能調用一次,如果不調用,則會自動生成一個響應頭。

(2)response.write(data,[encoding]):向請求的客戶端發送響應內容,data是一個buffer或者字符串,表示要發送的內容,如果data是字符串,那麽需要指定encoding編碼方式,默認是utf-8,在response.end之前調用,response.write可以被調用多次;

(3)response.end([data],[encoding]):結束響應,告知客戶端所有發送已經完成,當所有要返回的內容發送完畢的時候,該函數必須調用一次,他可接受兩個參數,意義和response.write相同,如果不調用該函數,客戶端將永遠處於等待狀態;

創建HTTP客戶端

http提供了兩個函數==http.request==和==http.get==,作為客戶端向HTTP服務器發起請求;

1. http.request(options[, callback])

const postData = querystring.stringify({
  ‘msg‘ : ‘Hello World!‘
});

const options = {
  hostname: ‘www.google.com‘,
  port: 80,
  path: ‘/upload‘,
  method: ‘POST‘,
  headers: {
    ‘Content-Type‘: ‘application/x-www-form-urlencoded‘,
    ‘Content-Length‘: Buffer.byteLength(postData)
  }
};

const req = http.request(options, (res) => {
  console.log(`狀態碼: ${res.statusCode}`);
  console.log(`響應頭: ${JSON.stringify(res.headers)}`);
  res.setEncoding(‘utf8‘);
  res.on(‘data‘, (chunk) => {
    console.log(`響應主體: ${chunk}`);
  });
  res.on(‘end‘, () => {
    console.log(‘響應中已無數據。‘);
  });
});

req.on(‘error‘, (e) => {
  console.error(`請求遇到問題: ${e.message}`);
});

// 寫入數據到請求主體
req.write(postData);
req.end();

註意,在例子中調用了 req.end()。 使用 http.request() 必須總是調用 req.end() 來表明請求的結束,即使沒有數據被寫入請求主體。

如果請求過程中遇到任何錯誤(DNS 解析錯誤、TCP 級的錯誤、或實際的 HTTP 解析錯誤),則在返回的請求對象中會觸發 ‘error‘ 事件。 對於所有的 ‘error‘ 事件,如果沒有註冊監聽器,則拋出錯誤。

以下是需要註意的幾個特殊的請求頭。

  • 發送 ‘Connection: keep-alive‘ 會通知 Node.js,服務器的連接應一直持續到下一個請求。

  • 發送 ‘Content-Length‘ 請求頭會禁用默認的塊編碼。

  • 發送 ‘Expect‘ 請求頭會立即發送請求頭。 通常情況下,當發送 ‘Expect: 100-continue‘ 時,超時時間與 continue 事件的監聽器都需要被設置。 詳見 RFC2616 章節 8.2.3。

  • 發送 Authorization 請求頭會替代 auth 選項計算基本身份驗證。

    2. http.get(options[, callback])

    該對象在HTTP服務器內部被創建。作為第二個參數唄傳入‘request‘事件
  • 接收與http.request()相同的設置。 method一直設置為GET,忽略繼承自原型的屬性

  • 返回: <http.ClientRequest>
http.get(‘http://nodejs.org/dist/index.json‘, (res) => {
  const { statusCode } = res;
  const contentType = res.headers[‘content-type‘];

  let error;
  if (statusCode !== 200) {
    error = new Error(‘請求失敗。\n‘ +
                      `狀態碼: ${statusCode}`);
  } else if (!/^application\/json/.test(contentType)) {
    error = new Error(‘無效的 content-type.\n‘ +
                      `期望 application/json 但獲取的是 ${contentType}`);
  }
  if (error) {
    console.error(error.message);
    // 消耗響應數據以釋放內存
    res.resume();
    return;
  }

  res.setEncoding(‘utf8‘);
  let rawData = ‘‘;
  res.on(‘data‘, (chunk) => { rawData += chunk; });
  res.on(‘end‘, () => {
    try {
      const parsedData = JSON.parse(rawData);
      console.log(parsedData);
    } catch (e) {
      console.error(e.message);
    }
  });
}).on(‘error‘, (e) => {
  console.error(`錯誤: ${e.message}`);
});

3. http.ClientRequest

該對象在http.request()內部被創建並返回。它表示著一個正則處理的請求,其請求頭已進入隊列。請求頭仍可使用 setHeader(name, value)、getHeader(name) 和 removeHeader(name) 進行修改。實際的請求頭會與第一個數據塊一起發送或當調用request.end()時發送。

  • connect事件
const http = require(‘http‘);
const net = require(‘net‘);
const url = require(‘url‘);

// 創建一個 HTTP 代理服務器
const proxy = http.createServer((req, res) => {
  res.writeHead(200, { ‘Content-Type‘: ‘text/plain‘ });
  res.end(‘okay‘);
});
proxy.on(‘connect‘, (req, cltSocket, head) => {
  // 連接到一個服務器
  const srvUrl = url.parse(`http://${req.url}`);
  const srvSocket = net.connect(srvUrl.port, srvUrl.hostname, () => {
    cltSocket.write(‘HTTP/1.1 200 Connection Established\r\n‘ +
                    ‘Proxy-agent: Node.js-Proxy\r\n‘ +
                    ‘\r\n‘);
    srvSocket.write(head);
    srvSocket.pipe(cltSocket);
    cltSocket.pipe(srvSocket);
  });
});

// 代理服務器正在運行
proxy.listen(1337, ‘127.0.0.1‘, () => {

  // 發送一個請求到代理服務器
  const options = {
    port: 1337,
    hostname: ‘127.0.0.1‘,
    method: ‘CONNECT‘,
    path: ‘www.google.com:80‘
  };

  const req = http.request(options);
  req.end();

  req.on(‘connect‘, (res, socket, head) => {
    console.log(‘已連接!‘);

    // 通過代理服務器發送一個請求
    socket.write(‘GET / HTTP/1.1\r\n‘ +
                 ‘Host: www.google.com:80\r\n‘ +
                 ‘Connection: close\r\n‘ +
                 ‘\r\n‘);
    socket.on(‘data‘, (chunk) => {
      console.log(chunk.toString());
    });
    socket.on(‘end‘, () => {
      proxy.close();
    });
  });
});

4. http.Agent

負責為HTTP客戶端管理連接的持續與復用。它為一個給定的主機和端口維護著一個等待請求的隊列,且為每個請求重復使用一個單一的socket連接直到隊列為空,此時socket會唄銷毀到一個連接池中等待被有著相同主機與端口的請求再次使用。 是否被銷毀或被放入連接池取決於 keepAlive 選項。

http.get(options, (res) => {
  // 處理事情
}).on(‘socket‘, (socket) => {
  socket.emit(‘agentRemove‘);
});

當 socket 觸發 ‘close‘ 事件或 ‘agentRemove‘ 事件時,它會被移出代理。 當打算長時間保持打開一個 HTTP 請求且不想它留在代理中,則可以如上處理

【node】node的核心模塊---http模塊,http的服務器和客戶端