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接收到,即注释的部分代码。
参考
了不起的Node.js
小结
时不我待。