2016-11-14 45 views
1

我试图演示一个HTTP服务器如何编码一个与tcp包最简单的方式。我这样做,前几次,但今天我面对一个意想不到的行为,从套接字对象的data事件是类似的要求解雇无论是一次或多次随机和我不知道为什么,以及如何修复它正确。nodejs tcp套接字发送多个数据事件

请注意,我知道我应该用处理数据,这是我在我的第二个示范做的流路。每一次都会增加复杂性,使演示文稿更容易​​遵循。

这是服务器。正如你所看到的,它很简单,容易获得。

const net = require('net') 

const response = `HTTP/1.1 200 OK 
Access-Control-Allow-Origin: * 
Foo: Bar 

foobar 
` 

net.createServer(socket => { 
    socket.on('data', buffer => { 
    console.log('----- socket data', Date.now()) 
    console.log(buffer.toString()) 
    socket.write(response) 
    socket.end() 
    console.log('-----') 
    }) 

    socket.on('end',() => console.log('----- socket end.')) 
    socket.on('close',() => console.log('----- socket close.', '\n')) 
}).listen(2000) 

为了测试我的服务器,我只需打开任何Web浏览器来http://localhost:2000并得到响应;但使用以下的有效载荷(使用浏览器的JavaScript控制台),当有时数据事件将被触发两次,错误结束了,因为write/end过程不能进行第二次。

var xhr = new XMLHttpRequest(); 
xhr.open("POST", "/"); 
xhr.setRequestHeader("Content-Type", "application/json"); 
xhr.send(JSON.stringify({ foo: "bar" })); 

这里是从服务器日志的管理单元,如果有什么可以帮助:

----- socket data 1479133993862 
POST/HTTP/1.1 
Host: localhost:2000 
Connection: keep-alive 
Content-Length: 13 
Pragma: no-cache 
Cache-Control: no-cache 
Origin: http://localhost:2000 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 
Content-Type: application/json 
Accept: */* 
Referer: http://localhost:2000/ 
Accept-Encoding: gzip, deflate, br 
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,id;q=0.2,ms;q=0.2,ko;q=0.2 

{"foo":"bar"} 
----- 
----- socket end. 
----- socket close. 

----- socket data 1479133994515 
POST/HTTP/1.1 
Host: localhost:2000 
Connection: keep-alive 
Content-Length: 13 
Pragma: no-cache 
Cache-Control: no-cache 
Origin: http://localhost:2000 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 
Content-Type: application/json 
Accept: */* 
Referer: http://localhost:2000/ 
Accept-Encoding: gzip, deflate, br 
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,id;q=0.2,ms;q=0.2,ko;q=0.2 

{"foo":"bar"} 
----- 
----- socket end. 
----- socket close. 

----- socket data 1479133995166 
POST/HTTP/1.1 
Host: localhost:2000 
Connection: keep-alive 
Content-Length: 13 
Pragma: no-cache 
Cache-Control: no-cache 
Origin: http://localhost:2000 
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36 
Content-Type: application/json 
Accept: */* 
Referer: http://localhost:2000/ 
Accept-Encoding: gzip, deflate, br 
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,id;q=0.2,ms;q=0.2,ko;q=0.2 


----- 
----- socket data 1479133995167 
{"foo":"bar"} 
events.js:154 
     throw er; // Unhandled 'error' event 
    ^

Error: write after end 
    at writeAfterEnd (_stream_writable.js:167:12) 
    at Socket.Writable.write (_stream_writable.js:212:5) 
    at Socket.write (net.js:624:40) 
    at Socket.<anonymous> (/Users/julien/Temp/foo.js:14:12) 
    at emitOne (events.js:90:13) 
    at Socket.emit (events.js:182:7) 
    at readableAddChunk (_stream_readable.js:153:18) 
    at Socket.Readable.push (_stream_readable.js:111:10) 
    at TCP.onread (net.js:529:20) 

正如你所看到的,首创2个请求都很好,但第三一个被切成2不同的部分。请求的标题将在一个数据事件中,而主体在另一个数据事件中。

我有几个开发商讨论这一点,我们猜测这可能与我的操作系统,这是OSX塞拉利昂如果能够没关系的TCP堆栈。

我看不到任何其他的方式来修补它不是积累的缓冲区进入上部范围内声明的变量,然后使用丑陋计时器把戏它结了类似一个撤销setImmediate 东西。

var timer = false, data = ''; 
socket.on('data', buffer => { 
    data += buffer.toString(); 

    clearTimeout(timer); 
    timer = setTimeout(() => process(socket, data), 1) 
}) 

的问题很简单:我知道这个解决办法是在许多方面非常错误的,但我看不到其他不使用流或HTTP包。你能照亮我吗?

回答

2

这就是TCP的工作原理。 TCP是一个字节流。在应用层上没有包含边界(甚至请求)的数据包。连接一端的n字节写入调用可能导致另一端读取n个字节的读取调用。你必须做好准备,每次读取都会产生任意数量的字节(达到读取调用的缓冲区大小 - 但是当你在node.js中获取数据时,你无法影响这个数据)。如果您在应用程序级别需要数据包,则需要自己处理,例如通过将长度前缀的数据包写入流中。

然而,由于它已经通过其中的报头和主体结束HTTP协议定义HTTP不需要包的概念。

+0

不要误会我,我明白,completely.What我不明白的是事件之间的切的随机性,以及请求是如何始终切2个半头和身体之间的有效精度。 –

+1

客户端可能会写2个呼叫,一个用于标题,另一个用于主体。有时候这些都是以组合的方式发送的(如果报头的数据包之前没有发送过),有时则不会。 – Matthias247

+0

但是总的来说,作为流的消费者你应该没有关系,因为你不能依赖这里的任何行为。 – Matthias247