程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 用asio傳文件

用asio傳文件

編輯:關於C語言

看了幾天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 ";
    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_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() << " ";}
 
  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();
}


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved