首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Boost.Asio / Asio 實作檔案傳輸:同步 Client 與非同步 Server 完整範例

     編輯:關於C語言

設計架構

  • Client(Sender):同步阻塞 I/O,簡單直接,適合單一檔案上傳
  • Server(Receiver):非同步 I/O,支援多連線並發,採用 boost::enable_shared_from_this 管理 Session 生命週期
  • 協定:自訂二進制 Header(檔案大小 + 檔名長度 + 檔名)+ 檔案內容流
  • 大檔案支援:使用 unsigned long long 突破 2GB 限制

共用 Header 定義


#ifndef _FILE_INFO_H_
#define _FILE_INFO_H_

#if USE_ASIO_ALONE
#include <asio.hpp>
#else
#include <boost/asio.hpp>
namespace asio { using namespace boost::asio; using boost::system::error_code; }
#endif

struct File_info {
    typedef unsigned long long Size_type;
    Size_type filesize;
    size_t filename_size;
    File_info() : filesize(0), filename_size(0) {}
};
#endif

Client 發送端


void sender(asio::io_service& io, const char* ip, unsigned port, const char* filename) {
    typedef asio::ip::tcp TCP;
    FILE* fp = fopen(filename, "rb");
    if (!fp) { std::cerr << "cannot open file\n"; return; }
    boost::shared_ptr<FILE> file_ptr(fp, fclose);  // RAII 確保關閉

    const size_t k_buffer_size = 32 * 1024;
    char buffer[k_buffer_size];
    File_info fi;
    int fn_size = strlen(filename) + 1;
    size_t header_size = sizeof(fi);
    if (header_size + fn_size > k_buffer_size) { std::cerr << "File name too long\n"; return; }
    fi.filename_size = fn_size;

    fseek(fp, 0, SEEK_END);
    fi.filesize = ftell(fp);
    rewind(fp);

    memcpy(buffer, &fi, header_size);
    memcpy(buffer + header_size, filename, fn_size);

    TCP::socket socket(io);
    socket.connect(TCP::endpoint(asio::ip::address_v4::from_string(ip), port));

    std::cout << "Sending: " << filename << " ";
    size_t len = header_size + fn_size;
    unsigned long long total = 0;
    clock_t start = clock();

    while (true) {
        socket.send(asio::buffer(buffer, len), 0);
        if (feof(fp)) break;
        len = fread(buffer, 1, k_buffer_size, fp);
        total += len;
    }

    double sec = double(clock() - start) / CLOCKS_PER_SEC;
    double speed = total / (1024.*1024.) / sec;
    std::cout << "Time: " << sec << "s  Bytes: " << total << "  Speed: " << speed << " MB/s\n";
}

Server 接收端

Session 類別(非同步狀態機)


class Session : public boost::enable_shared_from_this<Session> {
public:
    typedef asio::ip::tcp TCP;
    typedef asio::error_code Error;
    typedef boost::shared_ptr<Session> Pointer;
    typedef File_info::Size_type Size_type;

    static Pointer create(asio::io_service& io) { return Pointer(new Session(io)); }
    TCP::socket& socket() { return socket_; }

    void start() {
        clock_ = clock();
        std::cout << "Client: " << socket_.remote_endpoint().address() << "\n";
        async_receive_header();
    }

    ~Session() {
        if (fp_) fclose(fp_);
        double sec = double(clock() - clock_) / CLOCKS_PER_SEC;
        double speed = total_bytes_writen_ / (1024.*1024.) / sec;
        std::cout << "Done. Time: " << sec << "s  Bytes: " << total_bytes_writen_ << "  Speed: " << speed << " MB/s\n";
    }

private:
    Session(asio::io_service& io) : socket_(io), fp_(NULL), total_bytes_writen_(0), clock_(0) {}

    void async_receive_header() {
        socket_.async_receive(asio::buffer(&file_info_, sizeof(file_info_)),
            boost::bind(&Session::handle_header, shared_from_this(), asio::placeholders::error));
    }

    void handle_header(const Error& err) {
        if (err) return print_error(err);
        if (file_info_.filename_size > k_buffer_size) return print_error("Path too long");
        asio::async_read(socket_, asio::buffer(buffer_, file_info_.filename_size),
            boost::bind(&Session::handle_filename, shared_from_this(), asio::placeholders::error));
    }

    void handle_filename(const Error& err) {
        if (err) return print_error(err);
        const char* base = buffer_ + file_info_.filename_size - 1;
        while (base >= buffer_ && *base != '\\' && *base != '/') --base;
        ++base;
        std::cout << "Save as: " << base << "\n";
        fp_ = fopen(base, "wb");
        if (!fp_) return print_error("Open file failed");
        async_receive_content();
    }

    void async_receive_content() {
        socket_.async_receive(asio::buffer(buffer_, k_buffer_size),
            boost::bind(&Session::handle_content, shared_from_this(),
                        asio::placeholders::error, asio::placeholders::bytes_transferred));
    }

    void handle_content(const Error& err, size_t bytes) {
        if (err) {
            if (err != asio::error::eof) return print_error(err);
            if (total_bytes_writen_ != file_info_.filesize)
                std::cerr << "Size mismatch: " << total_bytes_writen_ << "/" << file_info_.filesize << "\n";
            return;
        }
        total_bytes_writen_ += fwrite(buffer_, 1, bytes, fp_);
        async_receive_content();
    }

    static void print_error(const Error& err) { std::cerr << err.message() << "\n"; }
    static void print_error(const char* msg) { std::cerr << msg << "\n"; }

    clock_t clock_;
    TCP::socket socket_;
    FILE* fp_;
    File_info file_info_;
    Size_type total_bytes_writen_;
    static const unsigned k_buffer_size = 32 * 1024;
    char buffer_[k_buffer_size];
};

Acceptor 主迴圈


class Tcp_server {
public:
    Tcp_server(asio::io_service& io, unsigned port)
        : acceptor_(io, TCP::endpoint(TCP::v4(), port)) { start_accept(); }
private:
    void start_accept() {
        Session::Pointer session = Session::create(acceptor_.get_io_service());
        acceptor_.async_accept(session->socket(),
            boost::bind(&Tcp_server::handle_accept, this, session, asio::placeholders::error));
    }
    void handle_accept(Session::Pointer session, const Error& err) {
        if (!err) session->start();
        start_accept();
    }
    TCP::acceptor acceptor_;
};

編譯與執行


# Server
g++ -std=c++11 -O2 -pthread server.cpp -lboost_system -o server
./server

# Client
g++ -std=c++11 -O2 client.cpp -lboost_system -o client
./client 127.0.0.1 bigfile.dat

注意:同步 Client 無超時機制,生產環境建議改非同步並加上 deadline_timer 實作超時控制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved