nodejs竟然无法中止http请求数据的传输
发布于 3 年前 作者 zengming00 2690 次浏览 来自 问答
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(),果然是无法中断的 那么问题来了: 怎么在数据传输的过程中得到了自己想要的数据时,中止网络连接?

7 回复
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

回到顶部