2012-07-12 53 views
2

我正在嘗試編寫一個在Ubuntu服務器上運行的遊戲服務器(無GUI),並且在步驟1時出現問題。我是C++新手,所以請忍受我。Boost ASIO - 如何編寫控制檯服務器2

我需要能夠在任何給定點輸入命令給服務器,同時繼續運行。由於cin是一個阻塞輸入,所以不會飛。我已經開始挖掘,看起來要走的路是使用Boost的ASIO庫。

This answer來令人難以置信的接近滿足我的需求,但我還需要知道兩兩件事:

1:輸入通過的「命令」,似乎在同一時間被限制爲1個字符。我需要多大的比單鍵輸入,如「關機」,更「listPlayers - 線上」等,我試圖適應代碼中使用的字符串,而不是字符「說的‘Hello World!’」:

#include <boost/asio.hpp> 

#include <boost/bind.hpp> 
#include <boost/enable_shared_from_this.hpp> 
#include <boost/shared_ptr.hpp> 

#include <iostream> 
#include <string> 

using namespace boost::asio; 

class Input : public boost::enable_shared_from_this<Input> 
{ 
public: 
    typedef boost::shared_ptr<Input> Ptr; 

public: 
    static void create(
      io_service& io_service 
      ) 
    { 
     Ptr input(
       new Input(io_service) 
       ); 
     input->read(); 
    } 

private: 
    explicit Input(
      io_service& io_service 
     ) : 
     _input(io_service) 
    { 
     _input.assign(STDIN_FILENO); 
    } 

    void read() 
    { 
     async_read(
       _input, 
       boost::asio::buffer(&_command, sizeof(_command)), 
       boost::bind(
        &Input::read_handler, 
        shared_from_this(), 
        placeholders::error, 
        placeholders::bytes_transferred 
        )); 
    } 

    void read_handler(
      const boost::system::error_code& error, 
      size_t bytes_transferred 
      ) 
    { 
     if (error) { 
      std::cerr << "read error: " << boost::system::system_error(error).what() << std::endl; 
      return; 
     } 

     if (_command.compare("\n") != 0) { 
      std::cout << "command: " << _command << std::endl; 
     } 

     this->read(); 
    } 

private: 
    posix::stream_descriptor _input; 
    std::string _command; 
}; 



int main() 
{ 
    io_service io_service; 
    Input::create(io_service); 
    io_service.run(); 
} 

但是,這會在輸入幾個字符後導致分割錯誤,並且在輸入任何輸入後按Enter鍵不再導致出現「command:」。有沒有辦法讓這個設置使用字符串?我確定將它們附加到一個單獨的字符串一次一個字符將工作,但我想這個設置將本機工作與整個字符串。

2 :(編輯澄清)我需要這個非阻塞輸入與我的其他服務器代碼一起工作。問題是:代碼去哪裏?我請你注意從上面的main()函數,修改爲使用while循環,並調用主循環功能:

bool loopControl = true; 

int main() 
{ 
    io_service io_service; 
    Input::create(io_service); 

    // This loops continually until the server is commanded to shut down 
    while(loopControl) 
    { 
     io_service.run();  // Handles async input 
     mainLoop();   // Where my actual program resides 
    } 
} 

即使一切工作,控制仍然永遠不會到達主循環()下正常情況下。換句話說,io_service.run()仍然阻塞,擊敗了整個目的。這顯然不是實現io_service和/或mainLoop()的正確方法;那是什麼?

我很抱歉,如果這已經做了幾千次,但顯然我沒有谷歌搜索正確的短語,以提出我期待的結果。

+0

不要提升官方的例子幫助你嗎? http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp或http://www.boost.org/doc/libs/1_38_0/doc/html/ boost_asio/examples.html – Timotei 2012-07-12 08:06:36

+0

不是特別的。雖然我確定它們對於能夠流利使用C++的人有用,但它對我來說信息超載。我幾乎不知道我所期望的效果是什麼術語,所以查找它的例子是有問題的。最重要的是,我無法確定代碼在做什麼,更不用說哪個部分有我想要的效果。總之,爲了理解提升的用法,我真的需要將它縮小到與我要做的事情非常接近的地方。 – drmuelr 2013-01-19 17:50:34

回答

0

boost::asio::buffer不直接支持從std::string創建可變緩衝區,主要是因爲它們不保證在內存預C++ 11中保持連續。

你調用它的方式((void*, size_t)重載),你會讓讀取覆蓋std :: string的內部,這會導致你的段錯誤。您應該使用此列表中的其他重載之一:http://www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/reference/buffer.html - 最有可能的一個用於std::vector<char>,因爲您可以在讀取返回時輕鬆地將其複製到字符串中。

現在的問題是,您需要事先知道要讀取多少個字符,因爲您的字符串長度可變。爲此,在閱讀實際內容之前,需要單獨設定長度。然後,您調整緩衝區的大小(正如我所說的,最有可能的是std::vector<char>)並計劃讀取該長度。請注意,發件人可以一起發送,這只是從流中讀取而複雜的...到summerize:

  1. async_read您的字符串長度爲固定長度
  2. 的某個整數調整所述內容緩衝區中讀取適當
  3. async_read您的內容

至於第二個問題,它是不是很清楚你想要什麼,但是如果你想在asio運行的時候做你自己的東西,你可能需要考慮io_service::poll()

0
  1. boost::asio::buffer(&_command, sizeof(_command))意味着你要覆蓋4個字節第一(或任何sizeof(string)是)_command對象,但是這顯然不是你想要的。如果您需要自動調整大小的輸入緩衝區,請改爲使用asio::streambuf

  2. io_service::run調用線程,所以你mainLoop將無法​​運行。您可以在單獨的線程中執行io_service::run,也可以手動輪詢io_serivce,將調用交叉至run_one/poll_one(請參閱參考資料),並使用您自己的應用程序循環的迭代。