以下内容摘自《Boost.Asio C++ Network Programming Cookbook》
异步TCP服务器是满足以下条件的分布式应用程序的一部分:
- 在客户端-服务器通信模型(c/s模型)中充当服务器
- 通过TCP协议与客户端应用程序通信
- 使用异步I / O和控制操作
- 可以同时处理多个客户
典型的异步TCP服务器根据以下算法工作:
- 分配一个接受套接字(acceptor socket),并将其绑定到特定的TCP端口。
- 启动异步接受操作。
- 生成一个或多个控制线程,并将其添加到运行Boost.Asio事件循环的线程池中。
- 异步接受操作完成后,启动一个新的操作以接受下一个连接请求。
- 启动异步读取操作以从连接的客户端读取请求。
- 异步读取操作完成后,处理请求并准备响应消息。
- 启动异步写入操作以将响应消息发送到客户端。
- 异步写入操作完成后,关闭连接并解除分配的套接字。
代码实现:
包含三个类:
- Service: 处理接受消息,并发送响应消息。
- Acceptor: 监听客户端请求连接, 并创建Service对象为客户端提供服务。
- Server: 服务器本身
#include
#include
#include
#include
#include
using namespace boost;
// 定义一个负责处理单个客户端的类,该类通过读取请求消息,对其进行处理然后向客户端发送响应消息来进行。
// 此类表示服务器应用程序提供的单个服务。 我们将其命名为Service:
class Service
{
public:
Service(std::shared_ptr sock) : m_sock(sock) {}
void StartHandling()
{
asio::async_read_until(*m_sock.get(),
m_request,
'\n',
[this](const boost::system::error_code& ec,
std::size_t bytes_transferred) {
onRequestReceived(ec, bytes_transferred);});
}
private:
void onRequestReceived(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
if (ec.value() != 0) {
std::cout << "Error occured! Error code = "
<< ec.value()
<< ". Message: " << ec.message();
onFinish();
return;
}
// 处理请求
m_response = ProcessRequest(m_request);
//启动异步写操作
asio::async_write(*m_sock.get(),
asio::buffer(m_response),
[this](const boost::system::error_code& ec,
std::size_t bytes_transferred)
{
onResponseSent(ec, bytes_transferred);
}
);
}
// 解析请求,进行处理,return回应消息
std::string ProcessRequest(asio::streambuf& request)
{
// 模拟消耗CPU的操作
int i = 0;
while (i != 1000000)
i++;
// 模拟阻塞线程的操作,比如,同步IO操作
std::this_thread::sleep_for(
std::chrono::milliseconds(100));
// return 回应消息
std::string response = "Response\n";
return response;
}
void onResponseSent(const boost::system::error_code& ec,
std::size_t bytes_transferred)
{
if (ec.value() != 0) {
std::cout << "Error occured! Error code = "
<< ec.value()
<< ". Message: " << ec.message();
}
onFinish();
}
void onFinish()
{
delete this;
}
std::shared_ptr m_sock;
std::string m_response;
asio::streambuf m_request;
};
// 该类代表一个 high-level acceptor 概念(与asio::ip::tcp::acceptor类所代表的low-level概念相比)
// 此类负责接受来自客户端的连接请求,并实例化Service类的对象,该类将向连接的客户端提供服务。
class Acceptor
{
public:
Acceptor(asio::io_service& ios, unsigned short port_num) :
m_ios(ios),
m_acceptor(m_ios,
asio::ip::tcp::endpoint(asio::ip::tcp::endpoint(
asio::ip::address_v4::any(),
port_num))),
m_isStopped(false)
{}
// 开始接受传入的连接请求
void Start()
{
m_acceptor.listen();
InitAccept();
}
// 停止接受传入的连接请求
void Stop() {
m_isStopped.store(true);
}
private:
void InitAccept()
{
std::shared_ptr
sock(new asio::ip::tcp::socket(m_ios));
m_acceptor.async_accept(*sock.get(),
[this, sock](
const boost::system::error_code& error)
{
onAccept(error, sock);
});
}
void onAccept(const boost::system::error_code& ec,
std::shared_ptr sock)
{
if (ec.value() == 0) {
(new Service(sock))->StartHandling();
}
else {
std::cout << "Error occured! Error code = "
<< ec.value()
<< ". Message: " << ec.message();
}
// Init next async accept operation if
// acceptor has not been stopped yet.
if (!m_isStopped.load()) {
InitAccept();
}
else {
// Stop accepting incoming connections
// and free allocated resources.
m_acceptor.close();
}
}
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
std::atomic m_isStopped;
};
// 该类代表服务器本身
class Server
{
public:
Server()
{
m_work.reset(new asio::io_service::work(m_ios));
}
void Start(unsigned short port_num, unsigned int thread_pool_size)
{
assert(thread_pool_size > 0);
acc.reset(new Acceptor(m_ios, port_num));
acc->Start();
// 创建具体数量的线程,并将其加入到线程池
for (unsigned int i = 0; i < thread_pool_size; i++) {
std::unique_ptr th(
new std::thread([this]()
{
// The run() function blocks until all work has finished and there are no more handlers to be dispatched,
// or until the io_context has been stopped.
// Multiple threads may call the run() function to set up a pool of threads from which the io_context may execute handlers.
// All threads that are waiting in the pool are equivalent
// and the io_context may choose any one of them to invoke a handler.
// A normal exit from the run() function implies that the io_context object is stopped (the stopped() function returns true).
// Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately unless there is a prior call to restart().
// Calling the run() function from a thread that is currently calling one of run(), run_one(), run_for(), run_until(), poll() or poll_one() on the same io_context object may introduce the potential for deadlock.
// It is the caller's reponsibility to avoid this.
// 从以上解释中,可以推断,可以在不同线程中调用run(),但是不可以在同一个线程中多次调用run(),
m_ios.run();
}));
m_thread_pool.push_back(std::move(th));
}
}
void Stop()
{
acc->Stop();
m_ios.stop();
for (auto& th : m_thread_pool) {
th->join();
}
}
private:
asio::io_service m_ios;
std::unique_ptr m_work;
std::unique_ptr acc;
std::vector> m_thread_pool;
};
const unsigned int DEFAULT_THREAD_POOL_SIZE = 2;
int main()
{
unsigned short port_num = 3333;
try {
Server srv;
// use the std::thread::hardware_concurrency() static method to obtain the number of processors.
// However, because this method may fail to do its job returning 0,
// we fall back to default value represented by the constant DEFAULT_THREAD_POOL_SIZE,
// which is equal to 2 in our case.
unsigned int thread_pool_size =
std::thread::hardware_concurrency() * 2;
if (thread_pool_size == 0)
thread_pool_size = DEFAULT_THREAD_POOL_SIZE;
srv.Start(port_num, thread_pool_size);
std::this_thread::sleep_for(std::chrono::seconds(60));
srv.Stop();
}
catch (system::system_error& e) {
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
return 0;
}