2017-06-10 76 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]) 
} 
相關問題