用asio传文件

from:点击打开链接

看了几天asio文档,总算可以写点小程序了。有些细节还是没弄明白,同步IO好像还不能设超时?服务器端采用异步IO,客户端则采用同步IO。传送文件,不得不注意到 C/C++ 2G文件限制,好像没通用的解决方法。

 

先定义下头文件,统一下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\n";
    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 << "\n";
  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\n"
    << "speed: " <<  speed << " MB/s\n\n"; 
}

int main(int args, char* argc[])
{
  if (args < 3) {
    std::cerr << "Usage: " << argc[0] << " ip_address  filename1 filename2 \n"; 
    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() << "\n";
    }
  }
}


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() << "\n";}
  
  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\n"
       << "speed: " <<  speed << " MB/s\n\n"; 
  }
  
  void start()
  {
    clock_ = clock();
    std::cout << "client: " << socket_.remote_endpoint().address() << "\n";
    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!\n";
      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_ << ")\n";
    
    fp_ = fopen(basename, "wb");
    if (fp_ == NULL) {
      std::cerr << "Failed to open file to write\n";
      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 << "\n";
      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() << "\n";}

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.\n";
  asio::io_service io;
  Tcp_server receiver(io, 1345);  
  io.run();
}





posted @ 2011-07-08 10:44  Podevor  阅读(532)  评论(0编辑  收藏  举报