echo服务器:服务器接收客户端消息再原封不动回传给客户端,可以保证服务器完整收到客户端消息包再回传完整信息
比如写gameserver的时候首先就需要一个echo知道两端通信是没问题的,还可以改造成一个时间戳的包
特点:
收到客户端消息长度是不确定的
以下是BOOST库中的例子:
#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)) {
//start();
}
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) {
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),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
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) {
// do_accept();
}
void start() {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
if (!ec) {
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 &e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
通过io_service.run()驱动异步事件
tcp::v4() = 0.0.0.0,程序本身不要做客户端连接限制,最好由运维在linux系统下进行设置
server类下通过自定义start()方法可以控制服务器启动时机
newSession表示对客户端socket的构造出了作用域会析构,session的内部调用了shared_from_this()引用计数会加1,所以newsession析构没有关系
asio中的socket支持move,move本身资源相当于恢复初始化,还保存了数据结构
使用move应该有构造函数的良好行为:
[this, self]的目的是为了让智能指针不析构,其中的self是自身类的拷贝,生命周期和lambda函数一样长,引用计数又会加1,此时引用计数为3
当self析构时减1,出了newsession作用域减1此时资源并未被析构,保存到lambda函数中去了
同理在do_write中auto self(shared_from_this());首先让自己的引用计数加1,因为在do_read()中处理完do_write()函数自身会被析构
就这样这个类的智能指针反复在do_read()和do_write()之间传递以防止被析构直到出错调用析构,出错就意味着客户端退出
在构造函数中不能使用enable_shared_from_this,因为自身类的构造都还没有完成,在析构函数中也不能使用enable_shared_from_this