用asio传文件
from:点击打开链接
看了几天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_reciver
//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(); }