看 asio 范例 async_tcp_echo_server.cpp 的过程学习到不少东西。
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "stdafx.h"
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include "asio.hpp"
using asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
// UMU: enable_shared_from_this 是为了在类内部使用指向自己的 shared_ptr
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
// UMU: 捕获 this 是为了在 lambda 表达式里调用 do_write
// UMU: 捕获 self 是为了在 lambda 表达式里引用它,增加引用计数器,防止未调用 lambda 函数前被释放
socket_.async_read_some(asio::buffer(data_, max_length),
[this, self](std::error_code ec, std::size_t length)
{
if (!ec) {
do_write(length);
// UMU
std::cout.write(data_, length);
std::cout << std::endl << std::endl;
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
asio::async_write(socket_, asio::buffer(data_, length),
[this, self](std::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(asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](std::error_code ec)
{
if (!ec) {
// UMU: std::move 是为了调用移动构造函数
// UMU: std::make_shared 是为了包装 new 对象和 new shared_ptr,使这两个 new 都控制在 make_shared 内部,这样如果任何一个 new 失败,都可以正确释放
std::make_shared<session>(std::move(socket_))->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;
}
asio::io_service io_service;
server s(io_service, std::atoi(argv[1]));
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
代码中的 self 只被 lambda 表达式捕获,在匿名函数体内部并没有被显式使用,但它是有被隐式引用到的,如果去掉对 self 的捕获,则在收到数据时,会发现 socket_ 已经失效了。
参考文章
- 《make_shared和shared_ptr的区别》http://www.tuicool.com/articles/F3u6jy
- 《std::enable_shared_from_this 有什么意义?https://www.zhihu.com/question/30957800
- 《c++11 条款21:尽量使用std::make_unique和std::make_shared而不直接使用new》http://blog.csdn.net/coolmeme/article/details/43405155