用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
#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";
}
}
}
#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();
}
#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();
}
作者: flyinghearts
出处: http://www.cnblogs.com/flyinghearts/
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。