2010-08-13 74 views
2

我使用boost:asio創建了一個服務器。當客戶端連接時,它發送一個file_size,file_name和file_data。服務器將其存儲在磁盤上的文件中。這完美的作品!雖然現在我在其應用程序的主線程中運行客戶端應用程序和服務器應用程序(所以我有一個服務器和客戶端應用程序),它阻止應用程序的其餘部分執行。如何:Boost :: asio的客戶端連接管理器?

所以在抽象我想創造這樣的事情:

服務器應用

  • 有一個線程來接收和處理所有傳入的文件傳輸
  • 有另一個線程在休息的應用程序可以做它想要的東西

cl ient應用

    當我按下空格鍵,或者只要我想,我想從主要的一個文件發送到服務器在一個單獨的線程,所以我的應用程序可以繼續做它需要做其他的東西

我的問題:如何爲我的客戶端文件傳輸創建管理器?

文件傳輸服務器接受新的文件傳輸客戶端連接

#include "ofxFileTransferServer.h" 

ofxFileTransferServer::ofxFileTransferServer(unsigned short nPort) 
    :acceptor(
     io_service 
     ,boost::asio::ip::tcp::endpoint(
      boost::asio::ip::tcp::v4() 
      ,nPort 
     ) 
     ,true 
    ) 
    ,port(nPort) 
{ 
} 

// test 
void ofxFileTransferServer::startThread() { 
    boost::thread t(boost::bind(
     &ofxFileTransferServer::accept 
     ,this 
    )); 
} 


void ofxFileTransferServer::accept() { 
    ofxFileTransferConnection::pointer new_connection(new ofxFileTransferConnection(io_service)); 
    acceptor.async_accept(
        new_connection->socket() 
        ,boost::bind(
         &ofxFileTransferServer::handleAccept 
         ,this 
         ,new_connection 
         ,boost::asio::placeholders::error 
        ) 
    ); 
    std::cout << __FUNCTION__ << " start accepting " << std::endl; 
    io_service.run(); 
} 


void ofxFileTransferServer::handleAccept(
      ofxFileTransferConnection::pointer pConnection 
      ,const boost::system::error_code& rErr 
) 
{ 
    std::cout << __FUNCTION__ << " " << rErr << ", " << rErr.message() << std::endl; 
    if(!rErr) { 
     pConnection->start(); 
     ofxFileTransferConnection::pointer new_connection(new ofxFileTransferConnection(io_service)); 
     acceptor.async_accept(
         new_connection->socket() 
         ,boost::bind(
          &ofxFileTransferServer::handleAccept 
          ,this 
          ,new_connection 
          ,boost::asio::placeholders::error 
         ) 
     ); 


    } 
} 

文件傳輸客戶端

#include "ofxFileTransferClient.h" 
#include "ofMain.h" 

using boost::asio::ip::tcp; 

ofxFileTransferClient::ofxFileTransferClient(
        boost::asio::io_service &rIOService 
        ,const std::string sServer 
        ,const std::string nPort 
        ,const std::string sFilePath 
):resolver_(rIOService) 
,socket_(rIOService) 
,file_path_(sFilePath) 
,server_(sServer) 
,port_(nPort) 
{ 
} 

ofxFileTransferClient::~ofxFileTransferClient() { 
    std::cout << "~~~~ ofxFileTransferClient" << std::endl; 
} 

void ofxFileTransferClient::start() { 
    // open file/get size 
    source_file_stream_.open(
        ofToDataPath(file_path_).c_str() 
        ,std::ios_base::binary | std::ios_base::ate 
    ); 
    if(!source_file_stream_) { 
     std::cout << ">> failed to open:" << file_path_ << std::endl; 
     return; 
    } 

    size_t file_size = source_file_stream_.tellg(); 
    source_file_stream_.seekg(0); 

    // send file size and name to server. 
    std::ostream request_stream(&request_); 

    request_stream << file_path_ << "\n" 
        << file_size << "\n\n"; 

    std::cout << ">> request_size:" << request_.size() 
       << " file_path: " << file_path_ 
       << " file_size: "<< file_size 
       << std::endl; 

    // resolve ofxFileTransferServer 
    tcp::resolver::query query(server_, port_); 
    resolver_.async_resolve(
       query 
       ,boost::bind(
         &ofxFileTransferClient::handleResolve 
         ,shared_from_this() 
         ,boost::asio::placeholders::error 
         ,boost::asio::placeholders::iterator 
       ) 
    ); 

} 


void ofxFileTransferClient::handleResolve(
       const boost::system::error_code& rErr 
       ,tcp::resolver::iterator oEndPointIt 
) 
{ 
    if(!rErr) { 
     tcp::endpoint endpoint = *oEndPointIt; 
     socket_.async_connect(
       endpoint 
       ,boost::bind(
         &ofxFileTransferClient::handleConnect 
         ,shared_from_this() 
         ,boost::asio::placeholders::error 
         ,++oEndPointIt 
       ) 
     ); 
    } 
    else { 
     std::cout << ">> error: " << rErr.message() << std::endl; 
    } 

} 

void ofxFileTransferClient::handleConnect(
       const boost::system::error_code& rErr 
       ,tcp::resolver::iterator oEndPointIt 
) 
{ 
    if(!rErr) { 
     cout << ">> connected!" << std::endl; 
     boost::asio::async_write(
       socket_ 
       ,request_ 
       ,boost::bind(
         &ofxFileTransferClient::handleFileWrite 
         ,shared_from_this() 
         ,boost::asio::placeholders::error 
       ) 
     ); 
    } 
    else if (oEndPointIt != tcp::resolver::iterator()) { 
     // connection failed, try next endpoint in list 
     socket_.close(); 
     tcp::endpoint endpoint = *oEndPointIt; 
     socket_.async_connect(
      endpoint 
      ,boost::bind(
       &ofxFileTransferClient::handleConnect 
       ,shared_from_this() 
       ,boost::asio::placeholders::error 
       ,++oEndPointIt 
      ) 
     ); 

    } 
    else { 
     std::cout << ">> error: " << rErr.message() << std::endl; 
    } 
} 

void ofxFileTransferClient::handleFileWrite(
       const boost::system::error_code& rErr 
) 
{ 
    if(!rErr) { 
     if(source_file_stream_.eof() == false) { 
      source_file_stream_.read(buf_.c_array(), buf_.size()); 
      if(source_file_stream_.gcount() <= 0) { 
       std::cout << ">> read file error." << std::endl; 
       return; 
      } 
      std::cout << ">> send: " << source_file_stream_.gcount() << " bytes, total: " << source_file_stream_.tellg() << " bytes\n"; 
      boost::asio::async_write(
        socket_ 
        ,boost::asio::buffer(buf_.c_array(), source_file_stream_.gcount()) 
        ,boost::bind(
         &ofxFileTransferClient::handleFileWrite 
         ,this 
         ,boost::asio::placeholders::error 
        ) 
      ); 

      if(rErr) { 
       std::cout <<">> send error: " << rErr << std::endl; // not sure bout this one.. 
      } 

     } 
     else { 
      return; // eof() 
     } 
    } 
    else { 
     std::cout << ">> error:" << rErr.message() << std::endl; 
    } 
} 

和一個小經理管理客戶端傳輸(這是在使用客戶端應用程序) 線程代碼僅用於tes目的並不被使用。

#include "ofxFileTransferManager.h" 

ofxFileTransferManager::ofxFileTransferManager() { 
} 

void ofxFileTransferManager::transferFile(
      const std::string sServer 
      ,const std::string nPort 
      ,const std::string sFile 
) 
{ 
    ofxFileTransferClient::pointer client(new ofxFileTransferClient(
     io_service_ 
     ,sServer 
     ,nPort 
     ,sFile 
    )); 
    client->start(); 
    io_service_.run(); 
} 

void ofxFileTransferManager::startThread() { 
    boost::thread t(boost::bind(
     &ofxFileTransferManager::run 
     ,this 
    )); 
} 

void ofxFileTransferManager::run() { 
    cout << "starting filemanager" << std::endl; 
    while(true) { 
     io_service_.run(); 
     boost::this_thread::sleep(boost::posix_time::milliseconds(250)); 
     cout << "."; 

    } 
    cout << "ready filemanager" << std::endl; 
} 

如果有人能幫助我在這裏,這將是太棒了。 boost的例子都使用「一次性」客戶端連接,這對我來說並沒有什麼幫助。

roxlu

回答

1

從我可以告訴,你真正需要的是創建一個新的線程,並把其主循環io_service.run();

很顯然,您必須注意保護appss主線程和asio線程之間共享的互斥鎖中的類和變量。

編輯:是這樣的嗎?

static sem_t __semSendFile; 

static void* asioThread(void*) 
{ 
    while(true) 
    { 
     sem_wait(&__semSendFile); 
     io_service.run(); 
    } 
    return NULL; 
} 

void ofxFileTransferManager::transferFile(
      const std::string sServer 
      ,const std::string nPort 
      ,const std::string sFile 
) 
{ 
    ofxFileTransferClient::pointer client(new ofxFileTransferClient(
     io_service_ 
     ,sServer 
     ,nPort 
     ,sFile 
    )); 
    client->start(); 
    sem_post(&__semSendFile); 
} 

int main(int argc, char **argv) 
{ 
    if (sem_init(&__semSendFile, 0, 0) != 0) 
    { 
     std::cerr << strerror(errno) << std::endl; 
     return -1; 
    } 

    pthread_t thread; 
    if (pthread_create(&thread, NULL, asioThread, NULL) != 0) 
    { 
     std::cerr << strerror(errno) << std::endl; 
     return -1; 
    } 

[...] 
+0

喜詹尼,爲沒關係的服務器,但我想用管理器文件「送」到這臺服務器類。問題是我不能把io_service.run()放入一個線程中,因爲它可能沒有文件需要傳輸。 – pollux 2010-08-13 14:43:59

4

太棒了!我只是想出了它。我不得不圍繞boost :: asio :: io_service :: work對象包裝我的io_service! (並忘記了shared_from_this())。我上傳我的代碼在這裏:http://github.com/roxlu/ofxFileTransfer

爲了方便起見,這裏是管理者代碼:

#include "ofxFileTransferManager.h" 



ofxFileTransferManager::ofxFileTransferManager() 
:work_(io_service_) 
{ 
} 

void ofxFileTransferManager::transferFile(
      const std::string sServer 
      ,const std::string nPort 
      ,const std::string sFile 
      ,const std::string sRemoteFile 
) 
{ 
    ofxFileTransferClient::pointer client(new ofxFileTransferClient(
     io_service_ 
     ,sServer 
     ,nPort 
     ,sFile 
     ,sRemoteFile 
    )); 
    client->start(); 
} 

void ofxFileTransferManager::startThread() { 
    boost::thread t(boost::bind(
     &ofxFileTransferManager::run 
     ,this 
    )); 
} 

void ofxFileTransferManager::run() { 
    io_service_.run(); 
} 
相關問題