用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();
}


 

你可能感兴趣的:(IO)