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