[英]Design pattern for bidirectional stream translator
我想將網絡協議實現對象設計為完全與套接字無關,並且僅充當雙向轉換器。 因此,協議對象應從“控制”端饋送對象或命令,並從“網絡”端發出字節,並從“網絡”端接受字節以轉換為對象/響應並從“控制”端發出。
我無法選擇優雅的設計模式在Node.js中執行此操作。 我希望它與Stream
完全兼容,到目前為止,我最終還是采用了這種方法:
socket = getSocketSomehow();
proto = new Protocol();
socket.pipe(proto.aux);
proto.aux.pipe(socket);
proto.write({ foo: 'this', bar: ['is', 'command'] });
proto.once('data', function(response) {
console.log('this is response: ' + response.quux);
});
proto
是兩個交叉連接的雙工流的集合,其本身是stream.Duplex
以及aux
。 傳入的網絡數據進入proto.aux
,然后被解析並作為對象從proto
發出。 傳入的對象進入proto
,並組成字節,並從proto.aux
發出。
有更好的方法來做同樣的事情嗎?
我以下面的方法結束了。 代碼示例包含在CoffeeScript中,以提高可讀性。
Bond
類實現了Duplex
流接口,但將兩個不相關的流綁定在一起,因此將讀寫操作代理到單獨的流。
'use strict'
{ EventEmitter } = require 'events'
class Bond extends EventEmitter
proxyReadableMethod = (method) =>
@::[method] = -> @_bondState.readable[method] arguments...
proxyWritableMethod = (method) =>
@::[method] = -> @_bondState.writable[method] arguments...
proxyReadableMethod 'read'
proxyReadableMethod 'setEncoding'
proxyReadableMethod 'resume'
proxyReadableMethod 'pause'
proxyReadableMethod 'pipe'
proxyReadableMethod 'unpipe'
proxyReadableMethod 'unshift'
proxyReadableMethod 'wrap'
proxyWritableMethod 'write'
proxyWritableMethod 'end'
constructor: (readable, writable) ->
super
@_bondState = {}
@_bondState.readable = readable
@_bondState.writable = writable
proxyEvent = (obj, event) =>
obj.on event, => @emit event, arguments...
proxyEvent readable, 'readable'
proxyEvent readable, 'data'
proxyEvent readable, 'end'
proxyEvent readable, 'close'
# proxyEvent readable, 'error'
proxyEvent writable, 'drain'
proxyEvent writable, 'finish'
proxyEvent writable, 'pipe'
proxyEvent writable, 'unpipe'
# proxyEvent writable, 'error'
module.exports = Bond
Protocol
聚合兩個內部Transform
流Parser
和Composer
。 Parser
從aux
端獲取數據,並將其轉換為從ctl
端輸出的數據,而Composer
則相反。 aux
和ctl
都是解析器和作曲者的紐帶,但方向不同-因此aux
只處理進出的“組合”數據,而ctl
端發出並接受“分析”的數據。 我的設計決定是通過Protocol
本身公開ctl
,而aux
作為實例變量可見。
Protocol
公開:
_parse
, _compose
作為_transform
類的方法 _parseEnd
, _composeEnd
作為_flush
的方法 parsed
, composed
為push
式方法 unparse
, uncompose
像unshift
的方法 'use strict'
Bond = require './bond'
BacklogTransform = require './backlog-transform'
class Protocol extends Bond
constructor: (options) ->
@_protocolState = {}
@_protocolState.options = options
parser = @_protocolState.parser = new ParserTransform @
composer = @_protocolState.composer = new ComposerTransform @
parser.__name = 'parser'
composer.__name = 'composer'
proxyEvent = (source, event) =>
source.on event, =>
@emit event, arguments...
proxyParserEvent = (event) =>
proxyEvent @_protocolState.parser, event
proxyComposerEvent = (event) =>
proxyEvent @_protocolState.composer, event
proxyParserEvent 'error'
proxyComposerEvent 'error'
super @_protocolState.parser, @_protocolState.composer
@aux = @_protocolState.aux = new Bond @_protocolState.composer, @_protocolState.parser
# @_protocolState.main = @main = new Bond @_protocolState.parser, @_protocolState.composer
parsed: (chunk, encoding) ->
@_protocolState.parser.push chunk, encoding
composed: (chunk, encoding) ->
@_protocolState.composer.push chunk, encoding
unparse: (chunk, encoding) ->
@_protocolState.parser.unshift chunk, encoding
uncompose: (chunk, encoding) ->
@_protocolState.composer.unshift chunk, encoding
#
_parse: (chunk, encoding, callback) ->
throw new TypeError 'not implemented'
_compose: (chunk, encoding, callback) ->
throw new TypeError 'not implemented'
_parseEnd: (callback) ->
callback()
_composeEnd: (callback) ->
callback()
class ParserTransform extends BacklogTransform
constructor: (@protocol) ->
options = @protocol._protocolState.options
super options, options.auxObjectMode, options.mainObjectMode
__transform: (chunk, encoding, callback) ->
@protocol._parse chunk, encoding, callback
__flush: (callback) ->
@protocol._parseEnd callback
class ComposerTransform extends BacklogTransform
constructor: (@protocol) ->
options = @protocol._protocolState.options
super options, options.mainObjectMode, options.auxObjectMode
__transform: (chunk, encoding, callback) ->
@protocol._compose chunk, encoding, callback
__flush: (callback) ->
@protocol._composeEnd callback
module.exports = Protocol
BacklogTransform
是實用程序類,它通過在_transform
期間的某個地方調用unshift
方法,將Transform
流擴展為具有將未轉換的塊移回隊列的_transform
,因此未移位的數據將出現在下一個_transform
, _transform
添加到新的塊中。 不幸的是,實現並不像我希望的那樣理想。
'use strict'
async = require 'async'
stream = require 'stream'
class BacklogTransform extends stream.Transform
constructor: (options, writableObjectMode, readableObjectMode) ->
options ?= {}
super options
@_writableState.objectMode = writableObjectMode ? options.writableObjectMode
@_readableState.objectMode = readableObjectMode ? options.readableObjectMode
@_backlogTransformState = {}
@_backlogTransformState.backlog = []
unshift: (chunk, encoding = null) ->
if @_writableState.decodeStrings
chunk = new Buffer chunk, encoding ? @_writableState.defaultEncoding
@_backlogTransformState.backlog.unshift { chunk, encoding }
_flushBacklog: (callback) ->
backlog = @_backlogTransformState.backlog
if backlog.length
if @_writableState.objectMode
async.forever(
(next) =>
return next {} if not backlog.length
{ chunk, encoding } = backlog.shift()
@__transform chunk, encoding, (err) ->
return next { err } if err?
next null
({ err }) ->
return callback err if err?
callback()
)
else
chunks = (chunk for { chunk, encoding } in backlog)
if @_writableState.decodeStrings
encoding = 'buffer'
chunk = Buffer.concat chunks
else
encoding = backlog[0].encoding
for item in backlog[1..]
if encoding != item.encoding
encoding = null
break
chunk = chunks.join ''
@_backlogTransformState.backlog = []
@__transform chunk, encoding, callback
else
callback()
_transform: (chunk, encoding, callback) ->
backlog = @_backlogTransformState.backlog
if backlog.length
backlog.push { chunk, encoding }
@_flushBacklog callback
else
@__transform chunk, encoding, callback
_flush: (callback) ->
@_flushBacklog =>
@__flush callback
__transform: (chunk, encoding, callback) ->
throw new TypeError 'not implemented'
__flush: (callback) ->
callback()
module.exports = BacklogTransform
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.