2014-03-05 17 views
0

我正在寫我的樹莓派一個程序,它由兩個主要部分之間的溝通:的C和PHP-計劃

    使用Spotify的-API
  1. A C-計劃「Libspotify」搜索音樂並播放它。
  2. 一個運行在apache2-Web服務器上的PHP程序,用於控制本地網絡中PC或智能手機的Spotify程序。

這兩個獨立程序之間的通信可以通過幾個文件進行。

該C部分,用於接收用戶輸入被調用一次的第二和是這樣工作的:

void get_input() { 
    int i = 0, c; 
    FILE *file; 
    struct stat stat; 
    char buffer[INPUT_BUFFER_SIZE]; 

    file = fopen(PATH_TO_COMMUNICATION, "r"); 
    if (file == NULL) { 
     perror("Error opening PATH_TO_COMMUNICATION"); 
     return; 
    } 

    fstat(fileno(file), &stat); 
    if (stat.st_size > 1) { 
     while((c = fgetc(file)) != EOF && i < INPUT_BUFFER_SIZE) { 
      buffer[i] = c; 
      ++i; 
     } 
     buffer[i] = '\0'; 
     parse_input(buffer); 
    } 

    fclose(file); 
    clear_file(PATH_TO_COMMUNICATION); 
} 

所以,通過在PHP fwrite(),我可以發送命令到C-程序。

然後,C程序解析輸入並將結果寫入「結果」文件中。一旦做到這一點,我寫的「溝通」的最後一個內容-file的「last_query」 -file,所以在PHP中我可以看到,當整個結果都寫在「結果」:

function search($query) { 
    write_to_communication("search ".$query); 
    do { // Wait for results 
     $file = fopen("../tmp/last_query", "r"); 
     $line = fgets($file); 
     fclose($file); 
     time_nanosleep(0, 100000000); 
    } while($line != $query); 
    echo get_result_json(); 
} 

它已經工作了,但我不喜歡這種方式。有很多投票和不必要的打開和關閉不同的文件。此外,在最糟糕的情況下,該程序需要超過一秒鐘,直到它開始對用戶輸入進行操作。此外,當C程序嘗試在PHP程序寫入文件時嘗試讀取文件時,可能會出現競爭狀況。

所以,現在我的問題:什麼是「正確」的方式,實現兩個程序部分之間的良好和清潔的溝通?如果沒有醜陋的投票和沒有競爭條件,是否有一些完全不同的方式?還是我可以改進現有的代碼,以便它變得更好?

回答

0

我想你自己寫了PHP和C代碼,並且你有權訪問編譯器等等?我所要做的並不是啓動一個不同的流程並使用進程間通信,而是編寫一個PHP C++擴展來完成這一切。

擴展啓動一個新線程,並且此線程從內存中的指令隊列中獲取所有指令。當你準備好接收結果時,最終的指令被髮送到線程(關閉的指令),當線程最終完成時,你可以拿起結果。

你可以使用這樣的方案:

#include <phpcpp.h> 
#include <queue> 
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <unistd.h> 

/** 
* Class that takes instructions from PHP, and that executes them in CPP code 
*/ 
class InstructionQueue : public Php::Base 
{ 
private: 
    /** 
    * Queue with instructions (for simplicity, we store the instructions 
    * in strings, much cooler implementations are possible) 
    * 
    * @var std::queue 
    */ 
    std::queue<std::string> _queue; 

    /** 
    * The final result 
    * @var std::string 
    */ 
    std::string _result; 

    /** 
    * Counter with number of instructions 
    * @var int 
    */ 
    int _counter = 0; 

    /** 
    * Mutex to protect shared data 
    * @var std::mutex 
    */ 
    std::mutex _mutex; 

    /** 
    * Condition variable that the thread uses to wait for more instructions 
    * @var std::condition_variable 
    */ 
    std::condition_variable _condition; 

    /** 
    * Thread that processes the instructions 
    * @var std::thread 
    */ 
    std::thread _thread; 


    /** 
    * Procedure to execute one specific instruction 
    * @param instruction 
    */ 
    void execute(const std::string &instruction) 
    { 
     // @todo 
     // 
     // add your own the implementation, for now we just 
     // add the instruction to the result, and sleep a while 
     // to pretend that this is a difficult algorithm 

     // append the instruction to the result 
     _result.append(instruction); 
     _result.append("\n"); 

     // sleep for a while 
     sleep(1); 
    } 

    /** 
    * Main procedure that runs the thread 
    */ 
    void run() 
    { 
     // need the mutex to access shared resources 
     std::unique_lock<std::mutex> lock(_mutex); 

     // keep looping 
     while (true) 
     { 
      // go wait for instructions 
      while (_counter == 0) _condition.wait(lock); 

      // check the number of instructions, leap out when empty 
      if (_queue.size() == 0) return; 

      // get instruction from the queue, and reduce queue size 
      std::string instruction(std::move(_queue.front())); 

      // remove front item from queue 
      _queue.pop(); 

      // no longer need the lock 
      lock.unlock(); 

      // run the instruction 
      execute(instruction); 

      // get back the lock for the next iteration of the main loop 
      lock.lock(); 
     } 
    } 


public: 
    /** 
    * C++ constructor 
    */ 
    InstructionQueue() : _thread(&InstructionQueue::run, this) {} 

    /** 
    * Copy constructor 
    * 
    * We just create a brand new queue when it is copied (copy constructor 
    * is required by the PHP-CPP library) 
    * 
    * @param queue 
    */ 
    InstructionQueue(const InstructionQueue &queue) : InstructionQueue() {} 

    /** 
    * Destructor 
    */ 
    virtual ~InstructionQueue() 
    { 
     // stop the thread 
     stop(); 
    } 

    /** 
    * Method to add an instruction 
    * @param params  Object representing PHP parameters 
    */ 
    void add(Php::Parameters &params) 
    { 
     // first parameter holds the instruction 
     std::string instruction = params[0]; 

     // need a mutex to access shared resources 
     _mutex.lock(); 

     // add instruction 
     _queue.push(instruction); 

     // update instruction counter 
     _counter++; 

     // done with shared resources 
     _mutex.unlock(); 

     // notify the thread 
     _condition.notify_one(); 
    } 

    /** 
    * Method to stop the thread 
    */ 
    void stop() 
    { 
     // is the thread already finished? 
     if (!_thread.joinable()) return; 

     // thread is still running, send instruction to stop (which is the 
     // same as not sending an instruction at all but just increasing the 
     // instruction counter, lock mutex to access protected data 
     _mutex.lock(); 

     // add instruction 
     _counter++; 

     // done with shared resources 
     _mutex.unlock(); 

     // notify the thread 
     _condition.notify_one(); 

     // wait for the thread to finish 
     _thread.join(); 
    } 

    /** 
    * Retrieve the result 
    * @return string 
    */ 
    Php::Value result() 
    { 
     // stop the thread first 
     stop(); 

     // return the result 
     return _result; 
    } 
}; 

/** 
* Switch to C context to ensure that the get_module() function 
* is callable by C programs (which the Zend engine is) 
*/ 
extern "C" { 
    /** 
    * Startup function that is called by the Zend engine 
    * to retrieve all information about the extension 
    * @return void* 
    */ 
    PHPCPP_EXPORT void *get_module() { 

     // extension object 
     static Php::Extension myExtension("InstructionQueue", "1.0"); 

     // description of the class so that PHP knows 
     // which methods are accessible 
     Php::Class<InstructionQueue> myClass("InstructionQueue"); 

     // add methods 
     myClass.method("add", &InstructionQueue::add); 
     myClass.method("result", &InstructionQueue::result); 

     // add the class to the extension 
     myExtension.add(std::move(myClass)); 

     // return the extension 
     return myExtension; 
    } 
} 

您可以使用此指令隊列從PHP腳本是這樣的:

<?php 

$queue = new InstructionQueue(); 

$queue->add("instruction 1"); 
$queue->add("instruction 2"); 
$queue->add("instruction 3"); 

echo($queue->result()); 

舉個例子,我只加了一個愚蠢的實現睡了一會兒,但你可以在那裏運行你的API調用函數。

該擴展使用PHP-CPP庫(請參閱http://www.php-cpp.com)。

+0

嗨!你必須透露這是你的圖書館。 http://meta.stackexchange.com/a/59302/155739請仔細閱讀本網站的常見問題。 –