var http = require('http');
var readline = require('readline');
var url = 'http://cnodejs.org/topic/581b2502e90cfbec054d763f';
function get(){
var req = http.get(url, function(res){ //这里res其实是一个IncomingMessage对象
res.setEncoding('utf-8');
var rl = readline.createInterface({input:res});
rl.on('line',function(line){
var patt = /浏览$/;
if(patt.exec(line)){
console.log(line.trim());
//以下均无法中止传输
res.socket.end();
res.socket.pause();
res.socket.destroy();
req.abort();
}
console.log(line);
});
}).on('error',function(e){
console.log(e);
});
}
get();
一段很简单的代码,只是获取页面中的浏览次数,预期的目标是得找到有浏览次数的行之后就中断数据的传输,可是我竟然找不到一种办法能够实现 在回调中res其实是一个IncomingMessage对象,看API,这个对象中是没有能够中断连接的方法的 但IncomingMessage提供了获取原始socket的方法,原始socket无论end()还是destroy()都无法中止,依然输出了整个网页! 那么我再找找,发现http.get()会返回一个 http.ClientRequest,虽然不太像能中断连接,但还是试了试它的abort(),果然是无法中断的 那么问题来了: 怎么在数据传输的过程中得到了自己想要的数据时,中止网络连接?
const net = require('net')
const options = {
host: 'cnodejs.org',
port: 80
}
const client = net.connect(options, () => {
console.log('connected to server!')
const request =
'GET /topic/581b2502e90cfbec054d763f HTTP/1.1\r\n' +
'Host: cnodejs.org\r\n' +
'Accept: */*\r\n\r\n'
client.write(request)
})
client.on('data', (data) => {
console.log(data.length);
var patt = /(\d+)\s*次浏览/
var str = data.toString('utf8')
var match = str.match(patt)
if (match) {
console.log('--------------------')
console.log(match[0])
client.destroy()
}
})
client.on('end', () => {
console.log('disconnected from server')
})
使用socket的destroy方法可以在”表面上”实现你所要的效果,但数据有可能还是已经从远端传送到了你的机器上,只是没有通过data
事件报上来而已
你关闭了本地连接并不代表对方不给你发送数据了,TCP是全双工的,有half open的概念,况且你就算shutdown,全关闭,你的关闭指令到达远端也需要时间 TCP还有个滑动窗口机制,只要在容忍范围内,对方会连续发送数据,而不是等你回应了ACK才继续发 所以在你处理完数据试图关闭的时候,已经晚了,因为远端发数据速度太快,已经无法收手了
要想细粒度的控制,只能通过原生的socket api了,用c++实现吧,在TCP头选项里,可以设置滑动窗口的大小,你设置小点,对方发数据就会慢点 这样的好处是流量节省了,但是速度变慢了
滑动窗口设置的再小理论上也解决不了楼主的问题,还是换个协议别用HTTP了
协议是双向的,单方改不了啊,举个例子,你想买个iphone的处理器,但是只有iphone卖,所以你定了iphone,想着买来拆出处理器,发货是发个手机
@qingfeng 别的语言也是这样的吗?比如java
@zengming00 与语言无关,与协议有关
@jiangzhuo 显然说的是TCP协议啊,我上面的代码也只是使用TCP实现了简单的HTTP GET 设置滑动窗口是有用的,你本地缓冲区满了,就不会给对方返回ACK了,那么对方就会暂停发送新数据,会重传旧数据,但是旧数据的重传速率和间隔也会动态调整,比如采取指数退避的原则 总之调整滑动窗口可以达到节省流量的目的 不过不建议这么做,除非你明确知道这么做带来的好处大于带来的坏处
我觉得楼主这个需求比较奇葩,本身只是下载一个html而已,数据量又不大,几十k,瞬间就发送完了,没必要控制的这么细,省不了多少
为什么不直接为cnodejs服务端贡献点代码呢,提供一个api,直接查询某个帖子的浏览数量 或者直接使用现成的api,get /api/v1/topics/:id