2017-06-22 57 views
1

我是一位57歲的AspNet程序員。因爲我是唯一一個在一開始就用C++工作的人,所以我的老闆們要求我爲一個需要具有非常特定特徵的溝通代理的客戶提供服務。它可以在多個平臺上作爲守護程序運行,並且有時可以是客戶端和服務器。我不夠了解,但我必須解決問題,並在Boost/Asio圖書館找到機會。服務器和客戶端同時使用Boost-Asio

我是Boost-Asio的新手,並閱讀文檔,我創建了一個服務器和一個TCP套接字客戶端,可以完美地交換消息並雙向全雙工。

我讀了幾篇文章,他們詢問我想要的東西,但所有答案都提示全雙工,就好像這意味着在同一個程序中有一個客戶端和一臺服務器。事實並非如此。全雙工的定義是指從同一連接寫入和讀取的能力,默認情況下每個TCP連接都是全雙工。

我需要讓兩個程序可以接受另一個發起的連接。這兩個方案之間不會有永久性的聯繫。有時候他們中的一個會要求連接,而另一個則會提出這個請求,並且都需要監聽,接受連接,交換一些消息並終止連接,直到發出新的請求。

我所做的服務器似乎陷入了偵聽端口的過程中,看看是否有連接進入,並且我無法繼續進程以便能夠創建套接字並請求與其他端口的連接程序。我需要線索,但我對他們不夠了解。

這可能嗎?

正如我所說我是Boost/Asio的新手,我試圖遵循一些線程和協程的文檔。然後,我把客戶端代碼在一個方法和服務器在另一:

int main(int argc, char* argv[]) 
{ 
    try 
    { 
     boost::thread t1(&server_agent); 
     boost::thread t2(&client_agent); 

     // wait 
     t1.join(); 
     t2.join(); 
     return 0;  
    } 
    catch (std::exception& e) 
    { 
     std::cerr << "Exception: " << e.what() << "\n"; 
    }  
    return 0; 
} 

和兩個協程:

void client_agent() { 
    parameters param; 
    param.load(); 

    boost::asio::io_service io_service1; 
    tcp::resolver resolver(io_service1); 
    char port[5]; 
    _itoa(param.getNrPortaServComunic(), port, 10); 
    auto endpoint_iterator = resolver.resolve({ param.getIPServComunicPrincipal(), port }); 
    std::list<client> clients; 
    client c(io_service1, endpoint_iterator, param); 

    while (true) 
    { 
     BOOL enviada = FALSE; 
     while (true) { 
      if (!enviada) { 
       std::cout << "sending a message\n"; 
       int nr = 110; 
       message msg(nr, param); 
       c.write(msg); 
       enviada = TRUE; 
      } 
     } 
    } 

    c.close(); 
} 

void server_agent() { 

    parameters param; 
    param.load(); 

    boost::asio::io_service io_service1; 
    std::list<server> servers; 
    tcp::endpoint endpoint(tcp::v4(), param.getNrPortaAgenteServ()); 
    servers.emplace_back(io_service1, endpoint); 
    io_service1.run(); 
} 

我用一個端口到客戶終端和其他端口服務器端點。這是對的嗎?需要?

它開始看起來像它會工作。每種方法都同時運行,但是隨後在io_service1.run(server_agent方法的最後一行)處發生線程分配錯誤:

boost :: exception_detail :: clone_impl>在內存位置0x0118C61C處。

有什麼建議嗎?

+1

至於標題,我敢肯定你可以做一個全雙工服務器/客戶端客戶端/服務器實施與升壓asio。只需爲進程和客戶端連接啓用'boost :: acceptor'即可。只是異步處理一切。 –

+0

謝謝。我會嘗試這種方法。這似乎是唯一不會陷入循環的方法。 – Neumann

回答

0

您正在描述一個UDP客戶端/服務器應用程序。但是你的實現必然會失敗。將asio服務器或客戶端視爲始終在單個線程中運行。

下面的代碼只是讓你有一個想法。我沒有試圖編譯它。客戶端非常相似,但可能需要傳輸緩衝區,顯然取決於應用程序。

這是一個縮短版本,所以你明白了。在最終的應用程序中,您希望添加接收超時等。 TCP服務器的原理與async_listen調用相同。連接的套接字可以存儲在shared_ptr中,並被lambdas捕獲,幾乎會神奇地被破壞。

服務器基本上是一樣的,除了沒有不斷的閱讀。如果在同一個進程中同時運行服務器和客戶端,則可以依靠run()因服務器而循環,但如果不是,則必須爲每個連接調用run()。 run()會在交換結束時退出。

using namespace boost::asio; // Or whichever way you like to shorten names 

class Server 
{ 
    public: 
    Server(io_service& ios) : ios_(ios) {} 

    void Start() 
    { 
     // create socket 
     // Start listening 
     Read(); 
    } 

    void Read() 
    { 
     rxBuffer.resize(1024) 
     s_.async_receive_from(
      buffer(rxBuffer), 
      remoteEndpoint_, 
      [this](error_code ec, size_t n) 
     { 
      OnReceive(ec, n); // could be virtual, if done this way 
     }); 
    } 

    void OnReceive(error_code ec, size_t n) 
    { 
     rxBuffer_.resize(n); 
     if (ec) 
     { 
      // error ... stops listen loop 
      return; 
     } 

     // grab data, put in txBuffer_ 
     Read(); 
     s_.async_send_to(
      buffer(txBuffer_), 
      remoteEndpoint_, 
      [this, msg](error_code ec, size_t n) 
     { 
      OnTransmitDone(ec, n); 
     }); 
    } 

    void OnTransmitDone(error_code ec, size_t n) 
    { 
    // check for error? 
    txBuffer_.clear(); 
    } 

    protected: 
    io_service& ios_; 
    ip::udp::socket s_; 
    ip::udp::endpoint remoteEndpoint_; // the other's address/port 
    std::vector<char> rxBuffer_;  // could be any data type you like 
    std::vector<char> txBuffer_;  // idem All access is in one thread, so only 
             // one needed for simple ask/respond ops. 
}; 

int main() 
{ 
    io_service ios; 
    Server server(ios); // could have both server and client run on same thread 
         // on same io service this way. 

    Server.Start(); 

    ios_run(); 
    // or std::thread ioThread([&](){ ios_.run(); }); 
    return 0; 
} 
+0

謝謝你,MichaëlRoy,你讓它看起來很簡單,我會盡量使用你的建議。 – Neumann

+0

我從上個月製作的應用程序中複製/粘貼,發現一些剩菜([this,msg])。這比處理select()或epoll/iocp要簡單得多。一個完整的可用於openwrt + windows的嵌入式客戶端+服務器應用程序在10天內完成。看門狗定時器和錯誤處理/套接字重置等等非常容易添加到這個模型中,並且不會真正地傷害可讀性。性能和CPU使用數量非常好。如果您有外部數據源,則需要添加一個tx隊列,但一旦基本框架就位後添加就相對簡單。 A ++ for boost.asio。 –