2010-08-13 95 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(); 
}