asioでファイルを転送する


from:クリックしてリンクを開く
数日asioドキュメントを見て、やっと小さなプログラムを書くことができました.詳細はまだ分からないが、同期IOはまだタイムアウトできないようだ.サーバ側は非同期IOを採用し,クライアント側は同期IOを採用する.ファイルを転送するには、C/C++2 Gファイルの制限に気づかざるを得ず、汎用的な解決策がないようです.
 
まず下ヘッダファイルを定義し、asioとboostを統一します.asio.
#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_sender
//www.cnblogs.com/flyinghearts

#include <iostream>
#include <cstdio>
#include <cstring>
#include <boost/shared_ptr.hpp>
#include "file_info.h"


void sender(asio::io_service& io, const char* ip_address, unsigned port, const char* filename)
{
  typedef asio::ip::tcp TCP;
  
  FILE *fp = fopen(filename, "rb");
  if (fp == NULL) {
    std::cerr << "cannot open file
"; return; } // , ,fclose 。 boost::shared_ptr<FILE> file_ptr(fp, fclose); clock_t cost_time = clock(); const size_t k_buffer_size = 32 * 1024; char buffer[k_buffer_size]; File_info file_info; int filename_size = strlen(filename) + 1; size_t file_info_size = sizeof(file_info); size_t total_size = file_info_size + filename_size; if (total_size > k_buffer_size) { std::cerr << "File name is too long"; return; } file_info.filename_size = filename_size; fseek(fp, 0, SEEK_END); file_info.filesize = ftell(fp); rewind(fp); memcpy(buffer, &file_info, file_info_size); memcpy(buffer + file_info_size, filename, filename_size); TCP::socket socket(io); socket.connect(TCP::endpoint(asio::ip::address_v4::from_string(ip_address), port)); std::cout << "Sending file : " << filename << "
"; size_t len = total_size; unsigned long long total_bytes_read = 0; while (true) { socket.send(asio::buffer(buffer, len), 0); if (feof(fp)) break; len = fread(buffer, 1, k_buffer_size, fp); total_bytes_read += len; } cost_time = clock() - cost_time; if (cost_time == 0) cost_time = 1; double speed = total_bytes_read * (CLOCKS_PER_SEC / 1024.0 / 1024.0) / cost_time; std::cout << "cost time: " << cost_time / (double) CLOCKS_PER_SEC << " s " << " transferred_bytes: " << total_bytes_read << " bytes
" << "speed: " << speed << " MB/s

"; } int main(int args, char* argc[]) { if (args < 3) { std::cerr << "Usage: " << argc[0] << " ip_address filename1 filename2
"; return 1; } asio::io_service io; for (int i = 2; i < args; ++i) { try { sender(io, argc[1], 1345, argc[i]); } catch (std::exception& err) { std::cerr << err.what() << "
"; } } }

server_reciver
//www.cnblogs.com/flyinghearts
#include <iostream>
#include <cstdio>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#include "file_info.h"

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 void print_asio_error(const Error& error) { std::cerr << error.message() << "
";} static Pointer create(asio::io_service& io) { return Pointer(new Session(io));} TCP::socket& socket() { return socket_; } ~Session() { if (fp_) fclose(fp_); clock_ = clock() - clock_; Size_type bytes_writen = total_bytes_writen_; if (clock_ == 0) clock_ = 1; double speed = bytes_writen * (CLOCKS_PER_SEC / 1024.0 / 1024.0) / clock_ ; std::cout << "cost time: " << clock_ / (double) CLOCKS_PER_SEC << " s " << "bytes_writen: " << bytes_writen << " bytes
" << "speed: " << speed << " MB/s

"; } void start() { clock_ = clock(); std::cout << "client: " << socket_.remote_endpoint().address() << "
"; socket_.async_receive( asio::buffer(reinterpret_cast<char*>(&file_info_), sizeof(file_info_)), boost::bind(&Session::handle_header, shared_from_this(), asio::placeholders::error)); } private: Session(asio::io_service& io) : socket_(io), fp_(NULL), total_bytes_writen_(0) { } void handle_header(const Error& error) { if (error) return print_asio_error(error); size_t filename_size = file_info_.filename_size; if (filename_size > k_buffer_size) { std::cerr << "Path name is too long!
"; return; } // async_read, async_read_some, , asio::async_read(socket_, asio::buffer(buffer_, file_info_.filename_size), boost::bind(&Session::handle_file, shared_from_this(), asio::placeholders::error)); } void handle_file(const Error& error) { if (error) return print_asio_error(error); const char *basename = buffer_ + file_info_.filename_size - 1; while (basename >= buffer_ && (*basename != '\\' && *basename != '/')) --basename; ++basename; std::cout << "Open file: " << basename << " (" << buffer_ << ")
"; fp_ = fopen(basename, "wb"); if (fp_ == NULL) { std::cerr << "Failed to open file to write
"; return; } receive_file_content(); } void receive_file_content() { socket_.async_receive(asio::buffer(buffer_, k_buffer_size), boost::bind(&Session::handle_write, shared_from_this(), asio::placeholders::error, asio::placeholders::bytes_transferred)); } void handle_write(const Error& error, size_t bytes_transferred) { if (error) { if (error != asio::error::eof) return print_asio_error(error); Size_type filesize = file_info_.filesize; if (total_bytes_writen_ != filesize) std::cerr << "Filesize not matched! " << total_bytes_writen_ << "/" << filesize << "
"; return; } total_bytes_writen_ += fwrite(buffer_, 1, bytes_transferred, fp_); receive_file_content(); } clock_t clock_; TCP::socket socket_; FILE *fp_; File_info file_info_; Size_type total_bytes_writen_; static const unsigned k_buffer_size = 1024 * 32; char buffer_[k_buffer_size]; }; class Tcp_server { public: typedef asio::ip::tcp TCP; typedef asio::error_code Error; Tcp_server(asio::io_service& io, unsigned port) : acceptor_(io, TCP::endpoint(TCP::v4(), port)) { start_accept(); } static void print_asio_error(const Error& error) { std::cerr << error.message() << "
";} 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& error) { if (error) return print_asio_error(error); session->start(); start_accept(); } TCP::acceptor acceptor_; }; int main() { std::cout << "Auto receive files and save then in current directory.
"; asio::io_service io; Tcp_server receiver(io, 1345); io.run(); }