1. 程式人生 > >node建立客戶端與伺服器端(HTTP)

node建立客戶端與伺服器端(HTTP)

Transfer-Encoding: chunked

  在我用telnet登入伺服器的時候,伺服器返回資訊如下。
這裡寫圖片描述
  其中,我們看到這樣一條相應–Transfer-Encoding: chunked。Transfer-Encoding頭資訊的預設值是chunked,主要的原因是Node天生的非同步機制,這樣響應就可以逐步產生。
  
  傳送資料塊的方式在涉及檔案系統的情況下會非常高效。Web伺服器對硬碟上的檔案託管服務是很常見的。因為Node允許以資料塊的形式往相應中寫資料,同時他又允許以資料塊的方式讀取檔案。所以我們就可以用ReadStream檔案系統API來實現讀取檔案。(本文討論的不是這個)

傳送一個簡單的HTTP請求

//服務端
require( 'http' ).createServer( function ( req, res ) {
  res.writeHead( 200 );
  res.end( 'hello world' );
}).listen( 4000 );

//客戶端
require( 'http' ).request({// 初始化一個新的http.Client Request物件
  host: '127.0.0.1',// 一定不能加http
  port: 4000,
  url: '/',// 隨意
  mehotd: 'GET'
}, function (res) {
  var
body = ''; res.setEncoding( 'utf-8' ); res.on( 'data', function (chunk) { body += chunk; }); res.on( 'end', function () { console.log( '\n We got: \033[96m' + body + '\033[39m'); }); }).end();

  上述程式碼中,首先呼叫了一個request方法。此方法用於初始化一個新的http.Client Request物件。注意的是,我們收集的資料是分塊的,連線的伺服器會返回不同的資料塊,只有所有的資料塊全部被收集到也能得到完整的相應。當然,也有可能所有的資料在一個data事件中到達了,我們無從而知。
  
  另一個重要問題是:這段程式碼在chunk都是ascii碼資料或者資料量比較少時是沒有問題,但如果你的資料是大量中文的話,恭喜你,中槍了,會出現亂碼。其原因是兩個chunk(Buffer物件)的拼接並不正常,相當於進行了buffer.toString() + buffer.toString()。如果buffer不是完整的,則toString出來後的string是存在問題的(比如一箇中文字被截斷)。具體可以參見樸靈寫得這篇文章:

http://cnodejs.org/topic/4faf65852e8fb5bc65113403
  
  在此例中,我們監聽end事件,然後將body輸出到控制檯。另外,我們通過相應物件設定編碼為utf-8,因為輸出的是文字。
  這裡寫圖片描述
  在上面的例子中,呼叫完request之後,還需要呼叫end。
  這是因為,在建立完一個請求之後,在傳送給伺服器前還可以和request物件進行互動。

querystring

  這是node的一個模組,用來對url引數進行解析(parse)和轉換為字串(stringify)。
  

var qs = require('querystring');
console.log( qs.stringify({name: 'real',age:20}) );
console.log( qs.stringify({學生: '皖林',age:20}) );
console.log( qs.parse( 'name=皖林&age=12' )); 

輸出結果為:

name=real&age=20
%E5%AD%A6%E7%94%9F=%E7%9A%96%E6%9E%97&age=20
{ name: '皖林', age: '12' }

對於要轉換為url引數字串的物件,其鍵和值都是經過編碼的,如果是中文,可以看到明顯區別。

客戶端

//客戶端
var
  http = require( 'http' ),
  qs = require( 'querystring' )
;

function send( theName ) {
  var data = qs.stringify({ name:theName });
  var request = require( 'http' ).request({// 初始化一個新的http.Client Request物件
    host: '127.0.0.1',
    port: 3000,
    url: '/',// 隨意
    mehotd: 'POST',
    headers: {   
      'Content-Type':'application/x-www-form-urlencoded',
      'Content-Length': data.length,  
    } 
  }, function ( res ) {
    res.setEncoding( 'utf-8' );
//  var body = '';
    res.on( 'data', function ( chunk ) { 
//    body += chunk;
    })
    res.on( 'end', function () {
//     console.log(body);
       console.log('\n  \033[90m request complete! \033[39m');
       process.stdout.write( '\n your name: ');
    });
  })
  request.end( data ) ;

}
process.stdout.write( '\n your name: ');// 客戶端啟動後,輸出your name
process.stdin.resume();// 等待輸入
process.stdin.setEncoding( 'utf-8' );// 設定輸入流編碼
process.stdin.on( 'data', function ( name ) {
  send( name.replace( '\n', '' ) );
});

  有幾個需要注意的地方:
  

  • request( {},function(res){} )的第一個引數需要需要顯式加上header。否則會報錯:Error: socket hang up
  • 儘管我的res.on( ‘data’ , function(){})中沒有做任何處理,但這應該是必需的(我嘗試過,沒有檢測data事件的時候,會出問題,問題是無法檢測到res.on( ‘end’, funciton(){})事件,我搜索了SO,說是將end換成close或finish有幫助。But,It still doesn’t work for me。我的node版本是v4.4.7。為此,我付出了2個小時的時間,甚至更多。)
  • 分塊問題,見上面。

服務端

var qs = require( 'querystring' );
require( 'http' ).createServer( function ( req, res ) {
  var body = '';
  req.on( 'data', function ( chunk ) { //
    body += chunk;
  })
  req.on( 'end', function (){//所有資料接收完畢
    res.writeHead( 200 );
    res.end( 'Done' );
    console.log( '\n got name: \033[90m' + qs.parse( body ).name + '\033[39m');
  })
}).listen( 3000 );

  服務端同樣也是按資料塊傳輸的。這裡的res.end( 'Done' );,在我的客戶端中可以使用body接收到,即註釋的部分程式碼。

參考

小結

  時不我待。