echo server是學(xué)習(xí)網(wǎng)絡(luò)編程必要的一個入門程序。
本例使用boost::asio提供的網(wǎng)絡(luò)庫實現(xiàn)一個簡單的異步的echo server路操。
技術(shù)原理:
1屯仗、使用std::shared_ptr管理session對象的生命周期
2丈牢、使用繼承enabled_shared_from_this來獲取當(dāng)前對象的智能指針己沛。
3、特別注意shared_from_this不能在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用垮卓,因為此時一個是對象沒有構(gòu)造完成粟按,第二個是對象已經(jīng)部分銷毀,不能獲取對象的智能指針灭将。
4庙曙、在do_read和do_write函數(shù)中使用值捕獲使智能指針對象的引用計數(shù)加1捌朴,延長到綁定的回調(diào)函數(shù)的生命周期后銷毀洼怔。
5镣隶、do_read和do_write相互調(diào)用矾缓,構(gòu)成調(diào)用鏈,使得io_service永遠存在io事件,io.run函數(shù)不會退出蜕依。
server端代碼友瘤,
main.cpp
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session: public std::enable_shared_from_this<session> {
public:
session(tcp::socket socket): socket_(std::move(socket)) {
// 不要在此處調(diào)用start, 因為對象還沒有構(gòu)造完成,不能調(diào)用
// shared_from_this生成智能指針
}
void start() {do_read(); }
private:
void do_read() {
auto self(shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data, max_length),
[this, self] (boost::system::error_code ec, std::size_t length) {
if(!ec) {
// 實際讀取多少字節(jié)就寫多少字節(jié)
do_write(length);
}
}
);
}
void do_write(std::size_t length) {
auto self(shared_from_this());
boost::asio::async_write(socket_,
boost::asio::buffer(data, length),
[self, this](boost::system::error_code ec, std::size_t length) {
if(!ec) {
// 調(diào)起回調(diào)鏈
do_read();
}
}
);
}
tcp::socket socket_;
enum {max_length=1024 };
char data[max_length];
};
class server {
public:
server(boost::asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service){
}
void start() {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
socket_,
[this](boost::system::error_code error) {
if(!error) {
auto newSession = std::make_shared<session>(std::move(socket_));
newSession->start();
}
do_accept();
}
);
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[]) {
try {
if(argc != 2) {
std::cerr << "Usage: async_tcp_echo_server [port] \n";
return 1;
}
boost::asio::io_service io_service;
server s(io_service, std::atoi(argv[1]));
s.start();
io_service.run();
}catch(std::exception& ex) {
std::cerr << ex.what() << std::endl;
}
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(main)
add_definitions(-std=c++14)
aux_source_directory(. CPP_LIST)
find_package(Boost REQUIRED COMPONENTS
system
filesystem
)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(main ${CPP_LIST})
target_link_libraries(main ${Boost_LIBRARIES})
target_link_libraries(main pthread)
client端柿究,client端是同步模式黄选,
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
enum {max_length=1024 };
int main(int argc, char* argv[]) {
try {
if(argc != 3) {
std::cout << "Usage: main [host] [port]\n";
return 1;
}
boost::asio::io_service io_service;
tcp::socket s(io_service);
tcp::resolver resolver(io_service);
// argv[1], argv[2] 這種寫法其實是用 初始化列表方式構(gòu)造一個 query對象
// resolver.resolve方法是同步的調(diào)用方法貌夕,非常低效,可能10秒才返回
// 實際應(yīng)用中盡量使用異步調(diào)用方法
boost::asio::connect(s, resolver.resolve({argv[1], argv[2]}));
std::cout << "Enter message:";
char request[max_length];
std::cin.getline(request, max_length);
std::size_t request_len = std::strlen(request);
boost::asio::write(s, boost::asio::buffer(request, request_len));
char reply[max_length];
std::size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply, request_len));
std::cout << "Reply is:";
std::cout.write(reply, reply_length);
std::cout << "\n";
}catch(std::exception& ex) {
std::cerr << ex.what() << std::endl;
}
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
project(main)
add_definitions(-std=c++14)
aux_source_directory(. CPP_LIST)
find_package(Boost REQUIRED COMPONENTS
system
filesystem
)
include_directories(${Boost_INCLUDE_DIRS})
add_executable(main ${CPP_LIST})
target_link_libraries(main ${Boost_LIBRARIES})
target_link_libraries(main pthread)
程序輸出如下