用asio传文件

 

    看了几天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_receiver
//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-07 22:57  flyinghearts  阅读(1253)  评论(0编辑  收藏