Boost实现FTP上传文件

#include "stdafx.h"
#include "ftp_client.h"

static boost::regex expression("^([0-9]+)(\\-| |$)(.*)$");
ftp_client::ftp_client() :cmd_socket_(io_service_),
work_(io_service_),data_socket_(io_service_),is_logined(false), is_connected(false)
{
    thread_ptr ptr(new boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_service_))));
}

ftp_client::~ftp_client()
{
    if (data_socket_.is_open()){
        data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
        data_socket_.close();
    }
    if (cmd_socket_.is_open()){
        cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
        cmd_socket_.close();
    }
}

bool ftp_client::connect(std::string host,  std::string port, std::string user, std::string passwd)
{
    if (is_connected){
        return true;
    }
    if (host.empty() || port.empty() || user.empty() || passwd.empty()){
        return false;
    }

    user_ = user; passwd_ = passwd; host_ = host; port_ = port;
    ip::tcp::resolver resolver(io_service_);
    ip::tcp::resolver::query query(host_, port_);
    ip::tcp::resolver::iterator it = resolver.resolve(query);

    cmd_socket_.async_connect( *it,
        boost::bind(&ftp_client::handle_connect,  
        this, boost::asio::placeholders::error));
    return true;
}
void ftp_client::upload_file(std::string filename)
{//async upload file
    try{
        boost::mutex::scoped_lock lock(iomutex);
        bool write_in_progress = !file_list.empty();
        file_list.push_back(std::make_pair(true, filename));
        if (!write_in_progress && is_logined){
            _write_message("PASV \r\n");
        }
    }catch(...){
        save_info((boost::format("%1%<%2%>: Remote is leave, session is close") %__FILE__ %__LINE__ ).str());
    }
}

string ftp_client::download_file(std::string filename)
{//sync download file
    try{
        boost::mutex::scoped_lock lock(cond_mutex);
        {
            boost::mutex::scoped_lock lock(iomutex);
            bool write_in_progress = !file_list.empty();
            file_list.push_back(std::make_pair(false, filename));
            if (!write_in_progress && is_logined){
                _write_message("PASV \r\n");
            }
        }
        condition.wait(lock);
    }catch(...){
        save_info(boost::format ("Remote is leave, session is close").str());
    }
    boost::filesystem::path path = boost::filesystem::current_path();
    path += "\\download";
    boost::filesystem::create_directory(path);
    path += "\\";
    path +=  boost::filesystem::path(filename).filename();
    return path.string();
}

void ftp_client::start()
{
    boost::asio::async_read_until(cmd_socket_,
        receive_msg, '\n', 
        boost::bind(&ftp_client::read_complete, this,
        boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void ftp_client::handle_connect(const boost::system::error_code& error)
{
    if (!error){
        is_connected = true;
        start();
    }else{
        is_logined = is_connected = false;
        save_info((boost::format("%1%<%2%>: %3%") %__FILE__ %__LINE__ %error.message()).str());
    }
}
void ftp_client::service(boost::asio::io_service &io_service)
{// not using
    while (1){// while io.run isn't have work, it will exit, so rerun it.
        boost::this_thread::interruption_point();
        boost::this_thread::sleep(boost::posix_time::milliseconds(100));
        io_service.run();
        io_service.reset();
    }
}
void ftp_client::paser_response(size_t resp_code, std::string resp_arg)
{
    boost::mutex::scoped_lock lock(iomutex);
    std::string cmd;
    std::string filename;
    switch (resp_code){
        case 220: //welcome
            cmd= "User " + user_ +"\r\n";
            _write_message(cmd);
            break;
        case 331:// enter password
            cmd = "Pass " + passwd_  +"\r\n";
            _write_message(cmd);
            break;
        case 230://loging success
            is_logined = true;
            if (!file_list.empty()){
                _write_message("PASV \r\n");
            }
            break;
        case 150://ftp server is reday for receive/trans data, client can  send/receive data
            {
                if (!file_list.empty()){
                    if(true == file_list.front().first){//uploadfile
                        if ((trans_data(file_list.front().second))){
                            std::cout << "file "<< file_list.front().second << "is upload to server\n";
                            file_list.pop_front();
                        }
                    }else{// download file;
                        if ((receive_data(file_list.front().second))){
                            std::cout << "file "<< file_list.front().second << "is download from server\n";
                        }
                        file_list.pop_front();
                        condition.notify_all();
                    }
                }
                break;
            }
        case 226: // Transfer complete.
            if (!file_list.empty()){//start trans file
                _write_message("PASV\n");
            }
            break;
        case 227://Entering passive Mode
            parser_pasv(resp_arg);
            if (file_list.empty()){
                break;
            }
            filename  = file_list.front().second;
            if (file_list.front().first){
                filename = filename.substr(filename.find_last_of('\\')+1);
                cmd = "STOR " +filename + "\r\n";
            }else{
                cmd = "RETR " + filename + "\r\n";
            }
            _write_message(cmd);
            break;
        case 530://Login or password incorrect!
            {
                _write_message("QUIT\r\n");
            }
        case 421:// connect timeout
            {
                is_logined = false;
                is_connected = false;
                if (cmd_socket_.is_open()){
                    cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
                    cmd_socket_.close();
                }
                condition.notify_all();
                connect(host_, port_, user_, passwd_);
                break;
            }
        case 550://File not found
            {
                if (!file_list.empty()) {
                    file_list.pop_front();
                    condition.notify_all();
                }
                break;
            }
        case 500:// oops 
        default:
            {
                save_info((boost::format("%1%<%2%>: Can not process Resp code:%3%, Resp args: %4%") 
                    %__FILE__ %__LINE__ %resp_code %resp_arg).str());
                if (data_socket_.is_open()){
                    data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
                    data_socket_.close();
                }
                is_logined = false;
                is_connected = false;
                if (cmd_socket_.is_open()){
                    cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
                    cmd_socket_.close();
                }
                connect(host_, port_, user_, passwd_);
                break;
            }
    }
}
void ftp_client::parser_pasv(std::string pasv)
{
    if ((pasv.find('(') == std::string::npos) && (pasv.find(')') == std::string::npos)){
        return;
    }
    pairporthost = parsehostport(pasv);

    ip::tcp::resolver resolver(io_service_);
    ip::tcp::resolver::query query(porthost.first, porthost.second);
    ip::tcp::resolver::iterator it = resolver.resolve(query);
    ip::tcp::endpoint endpoint = *it;
    if (data_socket_.is_open()){
        data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
        data_socket_.close();
    }
    data_socket_.connect(endpoint);
}
pair ftp_client::parsehostport(string s)
{
    size_t paramspos = s.find('(');
    string params = s.substr(paramspos + 1);
    size_t ip1pos = params.find(',');
    string ip1 = params.substr(0, ip1pos);
    size_t ip2pos = params.find(',', ip1pos + 1);
    string ip2 = params.substr(ip1pos + 1, ip2pos - ip1pos - 1);
    size_t ip3pos = params.find(',', ip2pos + 1);
    string ip3 = params.substr(ip2pos + 1, ip3pos - ip2pos - 1);
    size_t ip4pos = params.find(',', ip3pos + 1);
    string ip4 = params.substr(ip3pos + 1, ip4pos - ip3pos - 1);
    size_t port1pos = params.find(',', ip4pos + 1);
    string port1 = params.substr(ip4pos + 1, port1pos - ip4pos - 1);
    size_t port2pos = params.find(')', port1pos + 1);
    string port2 = params.substr(port1pos + 1, port2pos - port1pos - 1);

    pair hostport;
    hostport.first = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
    int portval = atoi(port1.c_str()) * 256 + atoi(port2.c_str());
    hostport.second = boost::lexical_cast(portval);
    return hostport;
}
bool ftp_client::trans_data(std::string filename)
{
    std::ifstream ifs(filename.c_str(), std::ifstream::binary);
    char buffer[1448];
    while (1){
        ifs.read(buffer, 1448);
        data_socket_.send(boost::asio::buffer(buffer,ifs.gcount()));
        if (ifs.gcount() < 1448){
            break;
        }
    }
    ifs.close();
    data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
    data_socket_.close();
    return true;
}

bool ftp_client::receive_data(std::string filename)
{
    boost::filesystem::path path = boost::filesystem::current_path();
    path += "\\download";
    boost::filesystem::create_directory(path);
    path += "\\";
    path +=  boost::filesystem::path(filename).filename();
    cout <:%3%") %__FILE__ %__LINE__ %e.what()).str());
    }catch (...){
        save_info((boost::format("%1%<%2%>:Ftp Receive data failed") %__FILE__ %__LINE__).str());
    }
    ofs.close();
    data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
    data_socket_.close();
    return true;
}
void ftp_client::read_complete(const boost::system::error_code& error, size_t bytes_transferred)
{
    if (!error) {
        boost::asio::streambuf::const_buffers_type bufs= receive_msg.data();
        std::string line(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs)+ bytes_transferred);
        receive_msg.consume(bytes_transferred);
        boost::cmatch what;
        if(boost::regex_match(line.c_str(), what, expression)){
            paser_response(::atoi(what[1].first), std::string(what[3].first, what[3].second));
        }
        start();
    }else{
        is_connected = false;
        save_info((boost::format("%1%<%2%>:%3%") %__FILE__ %__LINE__ % error.message() ).str());
    }
}


#include 
#include 
#include 

#define SINGLETON
class ftp_client:public boost::noncopyable
{
public:
    ftp_client();
    ~ftp_client();

    bool  connected(){return is_connected;}
#ifdef SINGLETON
    static ftp_client & getInstence()
    {
        static ftp_client ftp;
        return ftp;
    }
#endif 
    bool connect(std::string host,  std::string port, std::string user, std::string passwd);
    void upload_file(std::string filename);
    string download_file(std::string filename);
    string ftp_client::get_last_error(){
        boost::mutex::scoped_lock lock(iomutex);
        return error_msg;
    }
private:
    void start();
    void handle_connect(const boost::system::error_code& error);
    void read_complete(const boost::system::error_code& error, size_t bytes_transferred);

    void paser_response(size_t resp_code, std::string resp_arg);
    size_t write_message(std::string msg)
    {
        boost::mutex::scoped_lock lock(iomutex);
        return  _write_message(msg);
    }
    size_t _write_message(std::string msg)
    {
        boost::system::error_code ec;
        if (!cmd_socket_.is_open()){
            save_info((boost::format("%1%<%2%>: Server is not connect") %__FILE__ %__LINE__).str());
            return 0;
        }
        return boost::asio::write(cmd_socket_, boost::asio::buffer(msg),ec);
    }
    void parser_pasv(std::string pasv);
    pair parsehostport(string s);

    bool trans_data(std::string filename);
    bool receive_data(std::string filename);

    //not using it.
    void service(boost::asio::io_service &io_service);
    void save_info(const string info){
#ifdef  _DEBUG
        cout << info < > file_list;
    boost::mutex iomutex;
    enum{
        msg_head_len = 1024,
    };
    std::string user_;
    std::string passwd_;
    std::string host_;
    std::string port_;
    string error_msg;
};


你可能感兴趣的:(杂乱)