c++ - 如何:Boost::asio 的客户端连接管理器?

标签 c++ networking boost boost-asio

我使用 boost:asio 创建了一个服务器。当客户端连接时,它会发送文件大小、文件名和文件数据。服务器将其存储在磁盘上的文件中。这非常有效!虽然现在我在应用程序的主线程中运行客户端应用程序和服务器应用程序(因此我有一个服务器和客户端应用程序),这会阻止其余应用程序的执行。

所以抽象地我想创建这样的东西:

服务器应用

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

客户端应用

  • 当我按空格键时,或者每当我需要时,我想在与主线程不同的线程中将文件发送到服务器,以便我的应用程序可以继续执行它需要执行的其他操作。

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

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

#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 的示例全部使用“一次性”客户端连接,这并不能真正帮助我进一步。

罗克斯鲁

最佳答案

太棒了!我刚刚想通了。我必须将 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();
}

关于c++ - 如何:Boost::asio 的客户端连接管理器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3477407/

相关文章:

c++ - 如何从成员函数中获取 "simple"函数指针

http - 如何找到下载文件的 URL?

c++ - 在Boost Phoenix中获取局部变量的类型

c++ - Xcode:如何使用库设置 C++ 项目

c++ - 如何以编程方式禁用网络摄像头的自动对焦?

java - 有什么好的 ice4j 教程吗?

java - TCP数据包进入服务器,但没有进入监听程序

python - Boost.Python 返回引用现有 c++ 对象的 python 对象

c++ - boost::lockfree::queue 正在耗尽我的 CPU

c++ - 初始化列表的好处