2017-06-10 78 views
0

我正在封装一个我用NodeJS制作的可执行文件。可执行文件可以保存字符串以供可执行文件中的其他进程使用。每次可执行文件“保存”一个字符串时,它都会通过stdout将指针发送回服务器。 NodeJS服务器通过将字符串发送到可执行文件的stdin来保存字符串。所有回调都在同一时间执行,但为什么?

本来我正在写这样的代码:

CLRProcess.stdout.once('data',function(strptr){ 
    CLRProcess.stdout.once('data', function(str){ 
     console.log(str.toString()) 
    }) 
    CLRProcess.stdin.write("StringReturn " + strptr.toString()) 
}) 
CLRProcess.stdin.write("StringInject __CrLf__ Mary had a__CrLf__little lamb.") 

上面的代码注入的字符串

Mary had a 
little lamb. 

接收的指针串,然后在下一步骤中请求字符串,由将指针发送回主应用程序。

为了使编码算法更容易,我想这样的系统:

strPtr = Exec("StringInject __CrLf__ Mary had a__CrLf__little lamb.") 
str = Exec("StringReturn " + strPtr) 
// do stuff with str 

这是我做的代码:我打算这样做以下

class Pointer { 
    constructor(){ 
     this.value = undefined 
     this.type = "ptr" 
    } 
} 


class CLR_Events extends Array { 
    constructor(CLR){ 
     super() 
     this.CLR = CLR 
    } 
    runAll(){ 
     if(this.length>0){ 
      //Contribution by le_m: https://stackoverflow.com/a/44447739/6302131. See Contrib#1 
      this.shift().run(this.runAll.bind(this)) 
     } 
    } 
    new(cmd,args,ret){ 
     var requireRun = !(this.length>0) //If events array is initially empty, a run is required 
     var e = new CLR_Event(cmd,args,ret,this.CLR) 
     this.push(e) 
     if(requireRun){ 
      this.runAll() 
     } 
    } 
} 

class CLR_Event { 
    constructor(cmd,args,ret,CLR){ 
     this.command = cmd; 
     this.args = args 
     this.CLR = CLR 
     this.proc = CLR.CLRProcess; 
     this.ptr = ret 
    } 

    run(callback){ 
     //Implementing event to execute callback after some other events have been created. 
     if(this.command == "Finally"){ 
      this.args[0]() 
      console.log("Running Finally") 
      return callback(null) 
     } 

     //Implementation for all CLR events. 
     var thisEvent = this 
     this.proc.stdout.once('data',function(data){ 
      this.read() 
      data = JSON.parse(data.toString()) 
      thisEvent.ptr.value = data 
      callback(data); 
     }) 
     this.proc.stdin.write(this.command + " " + this._getArgValues(this.args).join(" ") + "\n"); 
    } 
    _getArgValues(args){ 
     var newArgs = [] 
     this.args.forEach(
      function(arg){ 
       if(arg.type=='ptr'){ 
        if(typeof arg.value == "object"){ 
         newArgs.push(JSON.stringify(arg.value)) 
        } else { 
         newArgs.push(arg.value) 
        } 
       } else if(typeof arg == "object"){ 
        newArgs.push(JSON.stringify(arg)) 
       } else { 
        newArgs.push(arg) 
       } 
      } 
     ) 
     return newArgs 
    } 
} 

var CLR = {} 
CLR.CLRProcess = require('child_process').spawn('DynaCLR.exe') 
CLR.CLRProcess.stdout.once('data',function(data){ 
    if(data!="Ready for input."){ 
     CLR.CLRProcess.kill() 
     CLR = undefined 
     throw new Error("Cannot create CLR process") 
    } else { 
     console.log('CLR is ready for input...') 
    } 
}) 
CLR.Events = new CLR_Events(CLR) 

//UDFs 

CLR.StringInject = function(str,CrLf="__CLR-CrLf__"){ 
    var ptr = new Pointer 
    this.Events.new("StringInject",[CrLf,str.replace(/\n/g,CrLf)],ptr) //Note CLR.exe requires arguments to be the other way round -- easier command line passing 
    return ptr 
} 
CLR.StringReturn = function(ptr){ 
    var sRet = new Pointer 
    this.Events.new("StringReturn",[ptr],sRet) 
    return sRet 
} 

CLR.Finally = function(callback){ 
    this.Events.new("Finally",[callback]) 
} 

  1. 函数StringInject,StringReturnFinally创建事件并追加它们到Events阵列。
  2. Events对象的runAll()函数从数组中移除第一个“事件”并运行数组的run()函数,并将其自身作为回调函数传递。
  3. 运行函数写入可执行文件的stdin,等待stdout中的响应,将数据追加到传入的指针中,然后执行传递给它的函数runAll()

这是我不明白...当执行多个字符串注射:

S_ptr_1 = CLR.StringInject("Hello world!") 
S_ptr_2 = CLR.StringInject("Hello world!__CLR-CrLf__My name is Sancarn!") 
S_ptr_3 = CLR.StringInject("Mary had a little lamb;And it's name was Doug!",";") 

我得到以下数据:

S_ptr_1 = {value:123,type:'ptr'} 
S_ptr_2 = {value:123,type:'ptr'} 
S_ptr_3 = {value:123,type:'ptr'} 

凡作为数据应该是:

S_ptr_1 = {value:1,type:'ptr'} 
S_ptr_2 = {value:2,type:'ptr'} 
S_ptr_3 = {value:3,type:'ptr'} 

我可以认为这会发生的唯一情况是if发生以下情况:

CLRProcess.stdin.write("StringInject Val1") 
CLRProcess.stdin.write("StringInject Val2") 
CLRProcess.stdin.write("StringInject Val3") 
CLRProcess.stdout.once('data') ==> S_ptr_1 
CLRProcess.stdout.once('data') ==> S_ptr_2 
CLRProcess.stdout.once('data') ==> S_ptr_3 

但是为什么?我是否忽略了某些东西,或者这种算法存在根本性错误?

+0

听起来你俯瞰事实'write'是异步的。另外,'新的指针'不会创建一个对象。您需要调用构造函数:'new Pointer()' – slebetman

+0

您只有一个CLR对象,因此只有一个DynaCLR.exe,因此每个stdin和stdout流只有一个。如果您要为每个呼叫创建一个新的DynaCLR,您可能会获得更多成功。 – GregHNZ

+0

@GregHNZ是的。要求只有1个标准输入/标准输出流。一切都应该由同一个DynaCLR可执行文件保存,因为我将使用它来编译VB和C#源代码。将它们存储在多个不同的进程中将不会允许这种可能性(不存储物理文件,这对我来说不可能)。使用stdin/out只是IPC的一种手段。 – Sancarn

回答

1

我发现了这个问题,并且我设法创建了一个解决方案。

的问题

CLR.StringInject被称为这就要求CLR_Events.new() CLR_Events.new()首先检查数组看它是否是空的,或者如果它有事件,创建一个新的事件,并推动事件的事件数组。如果数组最初是空的,则调用CLR_Events.runAll()

CLR_Events.runAll()然后除去CLR_Events.runAll()数组的第一个元素,并执行它设置侦听的STDOUT和STDIN写入数据。

接着的下一行代码运行:

CLR.StringInject被称为它调用CLR_Events.new() CLR_Events.new(),首先检查阵列看它是否是空的,或者如果它具有事件,它看到数组为空和所以调用runAll()

这就是问题。

runAll()将调用自身调用的事件数组中的下一个事件。但Events数组是始终为空,因为CLR_Events.new()不知道如何检查当前是否正在执行事件。它只检查数组中是否有事件。所以现在我们已经写了两次STDIN,并在STDOUT上设置了两个监听器。这链接到第三个命令最终意味着所有返回的对象都包含相同的数据。


的解决方案

为了解决这个问题,我不得不创建一个this.isRunning变量。

RunAll()只应调用如果isRunning ==假

isRunning只能是假的,当两个以下为真:

  1. 没有当前正在执行的事件调用
  2. Events.length == 0

IE回调后应该将isRunning设置为false。

这可确保所有事件在相同的回调循环中触发。

然后我有一些其他的问题,我的回调,因为thisundefined

function(){ 
    this.runAll.bind(this)() 
    if (!(this.length>0)) this.isRunning = false 
} 

为了解决这个问题,我有回调定义前添加一个CLREvents变量来存储this,并将其替换this回调中的CLREvents。现在它终于按预期工作。

全部工作代码:

class Pointer { 
    constructor(){ 
     this.value = undefined 
     this.type = "ptr" 
    } 
} 

class CLR_Events extends Array { 
    constructor(CLR){ 
     super() 
     this.CLR = CLR 
     this.isRunning = false 
    } 
    runAll(){ 
     console.log('RunAll') 
     this.isRunning = true 
     if(this.length>0){ 
      //Contribution by le_m: https://stackoverflow.com/a/44447739/6302131. See Contrib#1 
      var CLREvents = this 
      this.shift().run(function(){ 
       CLREvents.runAll.bind(CLREvents)() 
       if (!(CLREvents.length>0)) CLREvents.isRunning = false 
      }) 
     } 
    } 
    new(cmd,args,ret){ 
     console.log("New Event: " + JSON.stringify([cmd,args,ret]) + " - requireRun:" + (!(this.length>0)).toString()) 
     //If events array is initially empty, a run is required 
     var requireRun = !(this.length>0) 

     var e = new CLR_Event(cmd,args,ret,this.CLR) 
     this.push(e) 
     if(!this.isRunning){ 
      this.runAll() 
     } 
    } 
} 

class CLR_Event { 
    constructor(cmd,args,ret,CLR){ 
     this.command = cmd; 
     this.args = args 
     this.CLR = CLR 
     this.proc = CLR.CLRProcess; 
     this.ptr = ret 
    } 

    run(callback){ 
     console.log("RunOne") 
     //Implementing event to execute callback after some other events have been created. 
     if(this.command == "Finally"){ 
      this.args[0]() 
      console.log("Running Finally") 
      return callback(null) 
     } 

     //Implementation for all CLR events. 
     var thisEvent = this 
     this.proc.stdout.once('data',function(data){ 
      console.log('Callback') 
      this.read() 
      data = JSON.parse(data.toString()) 
      thisEvent.ptr.value = data 
      callback(data); 
     }) 
     this.proc.stdin.write(this.command + " " + this._getArgValues(this.args).join(" ") + "\n"); 
    } 
    _getArgValues(args){ 
     var newArgs = [] 
     this.args.forEach(
      function(arg){ 
       if(arg.type=='ptr'){ 
        if(typeof arg.value == "object"){ 
         newArgs.push(JSON.stringify(arg.value)) 
        } else { 
         newArgs.push(arg.value) 
        } 
       } else if(typeof arg == "object"){ 
        newArgs.push(JSON.stringify(arg)) 
       } else { 
        newArgs.push(arg) 
       } 
      } 
     ) 
     return newArgs 
    } 
} 

var CLR = {} 
CLR.CLRProcess = require('child_process').spawn('DynaCLR.exe') 
CLR.CLRProcess.stdout.once('data',function(data){ 
    if(data!="Ready for input."){ 
     CLR.CLRProcess.kill() 
     CLR = undefined 
     throw new Error("Cannot create CLR process") 
    } else { 
     console.log('CLR is ready for input...\n') 
     /* Example 1 - Using String Inject */ 
     S_ptr_1 = CLR.StringInject("Hello world!") 
     S_ptr_2 = CLR.StringInject("Hello world!__CLR-CrLf__My name is Sancarn!") 
     S_ptr_3 = CLR.StringInject("Mary had a little lamb;And it's name was Doug!",";") 
     console.log(S_ptr_1) 
     console.log(S_ptr_2) 
     console.log(S_ptr_3) 
    } 
}) 
CLR.Events = new CLR_Events(CLR) 

//UDFs 

CLR.StringInject = function(str,CrLf="__CLR-CrLf__"){ 
    var ptr = new Pointer 
    this.Events.new("StringInject",[CrLf,str.replace(/\n/g,CrLf)],ptr) //Note CLR.exe requires arguments to be the other way round -- easier command line passing 
    return ptr 
} 
CLR.StringReturn = function(ptr){ 
    var sRet = new Pointer 
    this.Events.new("StringReturn",[ptr],sRet) 
    return sRet 
} 

CLR.Finally = function(callback){ 
    this.Events.new("Finally",[callback]) 
} 
相关问题