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是存在問題的(比如一箇中文字被截斷)。具體可以參見樸靈寫得這篇文章:
在此例中,我們監聽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接收到,即註釋的部分程式碼。
參考
小結
時不我待。