On the receiver socket, the buffer from each individual 'data' event is not
guaranteed to be the same as the Buffer sent by the senders write()
call.
To get that you must use a Framing Protocol between the sender and receiver
(see FRAMING_PROTOCOLS.md).
This Frap
module provides just a framing protocol.
var svr = net.createServer().listen(7000)
svr.on('connection', function(sk){
var frap = new Frap(sk)
frap.on('data', function(buf) {
//simple echo
frap.write(buf)
})
})
var msg = {cmd: "print", args: ["hello", "world"]}
, sk = net.createConnection(7000, function() {
, frap = new Frap(sk)
frap.on('data', function(buf) {
var recv_msg = JSON.parse(buf.toString('utf8'))
console.log("recv:", msg)
sk.end()
})
frap.write(JSON.stringify(msg), 'utf8')
})
var sk = net.connect(PORT, function(){
var frap = new Frap(sk)
, basename = path.basename(FILENAME)
, namebuf = new Buffer(basename, 'utf8')
function sendFile(frap, filename, filesize) {
var rstream = fs.createReadStream(filename)
, wstream = frap.createWriteStream(filesize)
wstream.once('close', function(){
log("file, %s, sent", filename)
log("closing socket")
sk.end()
})
rstream.pipe(wstream)
}
frap.write(namebuf)
sendFile(frap, FILENAME, FILESTAT.size)
})
var svr = net.createServer(PORT, function(sk){
var frap = new Frap(sk)
var state = 'filename'
, filename
frap.on('data', function (buf) {
if (state !== 'filename') return
filename = buf.toString('utf8')
log("using filename, %s", filename)
state = 'filedata'
})
frap.on('header', function (rstream, framelen) {
if (state !== 'filedata') return
var dl_filename = path.join(DL_DIR, filename)
, wstream = fs.createWriteStream(dl_filename)
rstream.once('close', function(){
state = 'filename'
wstream.destroySoon()
})
rstream.pipe(wstream)
})
}
sk
is usually a net.Socket, but it can be any read/write Stream.
options
is an optional argument. It must be an object. The following
properties are recognized:
-
emit
is either'data'
,'frame'
, or'basic'
. It defaults to'data'
.If you set it to
'basic'
buffers will not be accumulated to emit'frame'
and/or'data'
events. There is two reasons for this: First, for a large frame, megabytes or gigabytes large, theFrap
object would have to collect the incoming Buffers until a complete frame was received. Second, in order for a'data'
event to be emitted those collected buffers would have to be joined into a new, rather large, Buffer; an expensive operation.
-
draining
Has value
true
when waiting for a'drain'
event,false
otherwise. -
writing
Has value
true
ifthis.draining
or aWriteFrameStream
is active,false
otherwise.
-
Event:
'data'
function (buf) { }
Where
buf
is a single Buffer object.'data'
events are the same as'frame'
events except that they have had the buffers concatenated into a single buffer. This concatenation operation is expensive so if there is no listeners for'data'
events the concatenation will not occur.write()
is the complement of'data'
events.Disabled if
options.emit === 'basic' or 'frame'
passed to constructor. -
Event:
'frame'
function (bufs, framelen)
Where
bufs
is an array of Buffer objects. andframelen
is an integer number of bytes of the frame size and the sum of the lengths of each of the Buffer objects inbufs
.sendFrame()
is the complement of'frame'
events.Disabled if
options.emit === 'basic'
is passed to constructor. You might want to disable'frame'
events to stop this library from accumulating very large number buffers in memory for very large frames. -
Event:
'header'
function (framelen) { }
Emitted when a header is encountered in the input stream.
Typically you only listen to this event when you want to treat an incoming frame as a stream. You do that by calling
createReadFrameStream()
inside the handler for this event. -
Event:
'part'
function (buf, pos, framelen) { }
Emitted for each buffer in the input stream.
pos
is the position where the buffer starts within the frame.framelen
is for the current processing frame.Typically end users do not listen to this event.
-
Event:
'drain'
function () { }
Emitted when the source stream flushes all the pending writes.
-
Event:
'error'
function (err) { }
A simple pass thru of any
'error'
event from the input data stream. -
Event:
'end'
function () { }
Emitted when
end()
is called. -
Event:
'close'
function (had_error) { }
Emitted when
destroy()
is called.
-
sendFrame(buf0, buf1, ..., bufN)
Arguments
buf0, buf1, ..., bufN
areBuffer
objects. The lengths of these buffers are summed to determine the frame length.This is can be more efficient than concatenating these buffers and using
write()
to send the whole large buffer as one frame.
-
write(buf)
-
write(str, enc)
Write a new frame with
buf.length
as the frame length. -
setEncoding(enc)
Inherited from FrapReader. All 'data' events will be converted from
Buffer
toString
viaBuffer#toString(enc)
. -
pause()
Suspend emitting
'data'
,'frame'
,'header'
or'part'
events. It will also callpause()
on the underlying source stream (sk
from the constructor). -
resume()
Resume emitting
'data'
,'frame'
,'header'
or'part'
events. It will also callresume()
on the underlying source stream (sk
from the constructor). -
pipe(dst)
A specialized pipe for
Frap
objects. Ifdst
is aFrap
object, then the currentFrap
object will be piped into thedst
Frap
object usingReadFrameStream
andWriteFrameStream
, rather than buffering up an entire frame and writing that into thedst
object. This is much more space efficient, though potentially slower until the frames get large enough where theRead/WriteFrameStream
overhead is less than theBuffer#concat()
overhead.If
dst
is not aninstanceof Frap
, then it will use the default Stream pipe implementation. -
end()
Stop the
Frap
object from emitting any new'data'
,'frame'
,'header'
or'part'
events and allow any unsent data to drain. -
destroy()
Close this Stream regardless of any unsent data. Emit a 'close' event.
-
destroySoon()
Close this Stream. Allow any unsent data to drain. Then Emit a 'close' event.
-
createReadFrameStream(framelen)
Create and return a RFrameStream object. Only one may exist at a time for each
Frap
object, otherwise it will throw anAssertionError
. -
createWriteFrameStream(framelen)
Create and return a WFrameStream object. Only one may exist at a time for each
Frap
object, otherwise it will throw anAssertionError
.
-
parse(buf)
Parse Buffer,
buf
, and emit resulting'data'
,'frame'
,'header'
, and'part'
events.