TCPソケットの性質を誤解していると思います。TCP は IP パケットを介してデータを送信しますが、TCP はパケット プロトコルではありません。TCP ソケットは単なるデータのストリームです。data
したがって、イベントを論理メッセージとして見るのは正しくありません。言い換えれば、一方の端での 1 つは、もう一方の端での単一のイベントsocket.write
と同等ではありません。data
data
ソケットへの 1 回の書き込みが 1:1 を 1 つのイベントにマップしない理由は多数あります。
- 送信者のネットワーク スタックは、複数の小さな書き込みを 1 つの IP パケットに結合する場合があります。(ネーグルアルゴリズム)
- サイズが 1 つのホップのMTUを超える場合、 IP パケットはその経路に沿ってフラグメント化(複数のパケットに分割) される場合があります。
- 受信側のネットワーク スタックは、複数のパケットを 1 つの
data
イベントに結合する場合があります (アプリケーションから見た場合)。
このため、1 つのdata
イベントに複数のメッセージ、単一のメッセージ、またはメッセージの一部のみが含まれる場合があります。
ストリーム経由で送信されたメッセージを正しく処理するには、完全なメッセージが得られるまで受信データをバッファリングする必要があります。
var net = require('net');
var max = 1024 * 1024 // 1 MB, the maximum amount of data that we will buffer (prevent a bad server from crashing us by filling up RAM)
, allocate = 4096; // how much memory to allocate at once, 4 kB (there's no point in wasting 1 MB of RAM to buffer a few bytes)
, buffer=new Buffer(allocate) // create a new buffer that allocates 4 kB to start
, nread=0 // how many bytes we've buffered so far
, nproc=0 // how many bytes in the buffer we've processed (to avoid looping over the entire buffer every time data is received)
, client = net.connect({host:'example.com', port: 8124}); // connect to the server
client.on('data', function(chunk) {
if (nread + chunk.length > buffer.length) { // if the buffer is too small to hold the data
var need = Math.min(chunk.length, allocate); // allocate at least 4kB
if (nread + need > max) throw new Error('Buffer overflow'); // uh-oh, we're all full - TODO you'll want to handle this more gracefully
var newbuf = new Buffer(buffer.length + need); // because Buffers can't be resized, we must allocate a new one
buffer.copy(newbuf); // and copy the old one's data to the new one
buffer = newbuf; // the old, small buffer will be garbage collected
}
chunk.copy(buffer, nread); // copy the received chunk of data into the buffer
nread += chunk.length; // add this chunk's length to the total number of bytes buffered
pump(); // look at the buffer to see if we've received enough data to act
});
client.on('end', function() {
// handle disconnect
});
client.on('error', function(err) {
// handle errors
});
function find(byte) { // look for a specific byte in the buffer
for (var i = nproc; i < nread; i++) { // look through the buffer, starting from where we left off last time
if (buffer.readUInt8(i, true) == byte) { // we've found one
return i;
}
}
}
function slice(bytes) { // discard bytes from the beginning of a buffer
buffer = buffer.slice(bytes); // slice off the bytes
nread -= bytes; // note that we've removed bytes
nproc = 0; // and reset the processed bytes counter
}
function pump() {
var pos; // position of a NULL character
while ((pos = find(0x00)) >= 0) { // keep going while there's a NULL (0x00) somewhere in the buffer
if (pos == 0) { // if there's more than one NULL in a row, the buffer will now start with a NULL
slice(1); // discard it
continue; // so that the next iteration will start with data
}
process(buffer.slice(0,pos)); // hand off the message
slice(pos+1); // and slice the processed data off the buffer
}
}
function process(msg) { // here's where we do something with a message
if (msg.length > 0) { // ignore empty messages
// here's where you have to decide what to do with the data you've received
// experiment with the protocol
}
}