HOW-TO:Boost :: asio的客户端连接管理器?

时间:2010-08-13 13:44:37

标签: c++ networking boost boost-asio

我使用boost:asio创建了一个服务器。当客户端连接时,它会发送file_size,file_name和file_data。服务器将其存储在磁盘上的文件中。这很完美!虽然现在我在他们的应用程序的主线程中运行客户端应用程序和服务器应用程序(所以我有一个服务器和客户端应用程序)阻止其他应用程序执行。

所以在摘要中我想创建这样的东西:

服务器应用

  • 有一个线程可以接收和处理所有传入的文件传输
  • 有另一个线程,其中应用程序的其余部分可以执行它想要的事情

客户端应用

  • 当我按空格键时,或者我想要的时候,我想在一个与主要文件分开的线程中将文件发送到服务器,这样我的应用程序就可以继续做其他需要做的事情了。

我的问题:如何为客户端文件传输创建管理器?

文件传输服务器接受新的文件传输客户端连接

#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;
    }
}

经理客户转移(在客户端应用中使用)的小经理 同样,线程代码仅用于测试目的,并未使用。

#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

2 个答案:

答案 0 :(得分:4)

大!我刚想通了。我不得不将我的io_service包装在boost :: asio :: io_service :: work对象周围! (并忘记了某处的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();
}

答案 1 :(得分: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;
    }

 [...]