2014-11-21 51 views
1

我正在監視和Minecraft服務器,我正在用Python編寫一個安裝文件。我需要能夠運行兩個線程,一個在控制檯窗口中運行minecraft_server.jar,另一個線程不斷檢查minecraft_server的輸出。另外,在啓動Java過程之後,我將如何從Python輸入到控制檯?Python:Java子進程和Python偵聽器之間的多線程?

例子:

thread1 = threading.Thread(target=listener) 
thread2 = minecraft_server.jar 

def listener(): 
    if minecraft_server.jarOutput == "Server can't keep up!": 
     sendToTheJavaProccessAsUserInputSomeCommandsToRestartTheServer 
+2

你是什麼意思「從Python輸入到控制檯」?您想將輸入提供給'minecraft_server.jar',就像您的程序是其控制檯一樣?或者將真正的控制檯的stdin與服務器共享,並以某種方式在兩者之間進行多路複用,以便獲得用戶輸入?要麼…? – abarnert 2014-11-21 00:19:09

+0

我想將輸入反饋到minecraft_server.jar,就好像我的程序在其控制檯中一樣。 @abarnert – TheMountainFurnaceGabriel 2014-11-21 00:34:19

回答

2

這是相當困難的在這裏說,但我覺得你問的是如何:

  • 在後臺啓動的程序。
  • 發送輸入,就好像它來自控制檯上的用戶。
  • 讀取它嘗試在控制檯上顯示給用戶的輸出。
  • 與此同時,運行另一個執行其他任務的線程。

最後一個很容易;事實上,你主要是寫它,你只需要在某個地方添加一個thread1.start()

subprocess模塊可讓您啓動程序並控制其輸入和輸出。如果你只想在所有輸入饋一次,等到它完成,然後再處理所有的輸出,但很明顯的是,這裏不是你的情況下,這是最簡單的,所以它更多的參與:

minecraft = subprocess.Popen(['java', 'path/to/minecraft_server.jar', '-other', 'args], 
          stdin=subprocess.PIPE, 
          stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 

我m將stdout和stderr合併成一個管道;如果你想單獨閱讀它們,或者發送stderr到/ dev/null,或者其他什麼,請參閱文檔;這很簡單。雖然我們在此做出假設,但我會假設minecraft_server使用簡單的基於行的協議,其中每個命令,每個響應和每條信息消息都只有一行(即在1K以下的文本以\n)。

現在,把它輸入,你只是這樣做:

minecraft.stdin.write('Make me a sandwich\n') 

或者,在Python 3.X:

minecraft.stdin.write(b'Make me a sandwich\n') 

要閱讀它的輸出,你這樣做:

response = minecraft.stdout.readline() 

這就像一個普通的文件。但請注意,它的作用類似於二進制文件。在Python 2.x中,唯一的區別是換行符不會自動轉換,但在Python 3.x中,這意味着您只能編寫bytes(和兼容對象),而不是str s,並且您將收到bytes。這有很好的理由,但是如果您想要獲取像文本文件那樣的管道,請參閱Frequently Used ArgumentsPopen Constructor下的universal_newlines(可能還有bufsize)參數。


而且,它就像一個阻止文件。對於一個普通的文件,這很少重要,但是對於一個管道來說,很可能以後會有數據,但是還沒有數據(因爲服務器還沒有寫入數據)。所以,如果還沒有輸出(或者沒有完整的產品線的價值,因爲我使用了readline()),那麼您的線程就會阻塞,等待。

如果你不想這樣做,你可能想創建另一個線程來服務stdout。其功能實際上看起來很相似,你有什麼:

def listener(): 
    for line in minecraft.stdout: 
     if line.strip() == "Server can't keep up!": 
      minecraft.stdin.write("Restart Universe\n") 

現在,線程可以阻止所有的一天,沒有任何問題,因爲你的其他線程仍在繼續。


嘛,不太沒有問題。

首先它很難徹底關閉你的程序。

更嚴重的是,進程之間的管道具有固定的大小;如果你沒有足夠快地服務標準輸出,或者孩子服務的標準輸入速度不夠快,管道可能會阻塞。而且,就像我寫的東西一樣,如果stdin管道阻塞,我們將永遠被阻塞在那個stdin.write中,並且不會到達下一個讀取的stdout,這樣也可以阻止,並且突然之間我們都在等待彼此永遠。

你可以通過讓另一個線程來服務stdout來解決這個問題。 subprocess模塊本身包含一個示例,在所有高級函數使用的Popen._communicate函數中。 (請務必查看Python 3.3或更高版本,因爲早期版本存在錯誤。)

如果您使用的是Python 3.4+(或3.3,關閉PyPI的backport),則可以改爲使用asyncio來重寫程序一個事件循環,並以與編寫基於反應器的網絡服務器相同的方式處理輸入和輸出。這就是2017年所有酷酷的孩子們都在做的事情,但在2014年年底,許多人仍然認爲這看起來很新,很可怕。


如果所有這聽起來像比你簽署了很多工作,你可能要考慮使用pexpect,它封裝了很多繁瑣的細節,並做了一些簡化的假設是可能真的在你的情況。

+0

正是我在尋找的,感謝這樣徹底的runthrough如何一切應該工作。 – TheMountainFurnaceGabriel 2014-11-21 00:57:26

+0

我正在嘗試使用minecraft.stdin.write(「say hello world \ n」),但我不斷收到一個類型錯誤,指出str不受緩衝區類型支持?任何想法爲什麼? 規格: Windows 7的 的Python 3.4.2 @abarnert – TheMountainFurnaceGabriel 2014-11-21 01:28:03

+0

@TheMountainFurnaceGabriel:你不知道'str'和'bytes',以及文本和二進制文件對象之間的差異?讓我編輯答案。 – abarnert 2014-11-21 01:30:37