Boost.Asio c++ 网络编程翻译(22)

服务端应用中的同步I/O

类似客户端,服务端也被分为两种情况用来匹配之前章节中的情况1和情况2。同样,两种情况都采用“发送请求-读取结果”的策略。

第一种情况是我们在之前章节实现过的同步服务端。当你是同步时读取一个完整的请求不是很简单,因为你需要避免阻塞(通常来说是能读多少就读多少):

void read_request() {
       if ( sock_.available())

}

already_read_ += sock_.read_some(
    buffer(buff_ + already_read_, max_msg - already_read_));

只要一个消息被完整读到,就对它进行处理然后回复给客户端:

void process_request() {
       bool found_enter = std::find(buff_, buff_ + already_read_, ‘\n‘)
                           < buff_ + already_read_;
       if ( !found_enter)
           return; // message is not full
       size_t pos = std::find(buff_, buff_ + already_read_, ‘\n‘) -
   buff_;
       std::string msg(buff_, pos);
     ...
       if ( msg.find("login ") == 0) on_login(msg);
       else if ( msg.find("ping") == 0) on_ping();
     else ...

}

如果我们想让我们的服务端变成一个推送服务端,我们通过如下的方式修改:

typedef std::vector<client_ptr> array;
   array clients;
   array notify;
   std::string notify_msg;
   void on_new_client() {
       // on a new client, we notify all clients of this event
       notify = clients;
       std::ostringstream msg;
       msg << "client count " << clients.size();
       notify_msg = msg.str();
       notify_clients();
   }
   void notify_clients() {
       for ( array::const_iterator b = notify.begin(), e = notify.end();
                                   b != e; ++b) {
           (*b)->sock_.write_some(notify_msg);
       }

}

on_new_client()方法是事件之一,这个事件我们需要通知素有的客户端。notify_clients是通知所有对一个事件感兴趣客户端的方法。它发送消息但是不等待每个客户端返回的结果,因为那样的话就会导致阻塞。当客户端返回一个结果时,客户端会告诉我们它为什么回复(然后我们就可以正确地处理它)。

同步服务端中的线程

这是一个非常重要的关注点:我们开辟多少线程去处理服务端请求?

对于一个同步服务端,我们至少需要一个处理新连接的线程:

void accept_thread() {
       ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp:
   :v4(),8001));
       while ( true) {
           client_ptr new_( new talk_to_client);
           acceptor.accept(new_->sock());
           boost::recursive_mutex::scoped_lock lk(cs);
           clients.push_back(new_);

} }

对于已经存在的客户端:

  • 我们可以是单线程。这是最简单的,同时也是我在第四章 同步服务端中采用的实现方式。它可以很轻松地处理100-200并发的客户端而且有时候会更多,对于大多数情况来说这已经足够用了。
  • 我们可以对每个客户端开一个线程。这不是一个很好的选择;他会浪费很多线程而且有时候会导致调试困难,而且当它需要处理200以上并发的客户端的时候,它可能马上会到达它的瓶颈。
  • 我们可以用一些固定数量的线程去处理已经存在的客户端

第三种选择是同步服务端中最难实现的;整个talk_to_client类需要是线程安全的。然后,你需要一个机制来确定哪个线程处理哪个客户端。对于这个问题,你有两个选择:

  • 将特定的客户端分配给特定的线程;比如,线程1处理前面20个客户端,线程2处理21到40个线程,等等。当一个线程在使用时(我们在等待在客户端阻塞的一些东西),我们从以存在客户端列表中将其取出来。等我们处理完之后,再把它放回到列表中。每个线程都会循环遍历已经存在的客户端列表,然后把拥有完整请求的第一个客户端提出来(我们已经从客户端读取了一条完整的消息),然后回复它。
  • 服务端可能会变得无响应
  1. 第一种情况,被同一个线程处理的几个客户端同时发送请求,因为一个线程在同一时刻只能处理一个请求。所以这种情况我们什么也不能做。
  2. 第二种情况,如果我们发现并发请求大于当前线程个数的时候。我们可以简单地创建新线程来处理当前的压力。

下面的代码片段有点类似之前的answer_to_client方法,它向我们展示了第二种方法的实现方式:

  1. struct talk_to_client : boost::enable_shared_from_this<talk_to_client>
       {
    
           ...
           void answer_to_client() {
    
               try {
                   read_request();
    
                   process_request();
               } catch ( boost::system::system_error&) {
    

    stop(); }

    } };

我们需要对它进行修改使它变成下面代码片段的样子:

  1. struct talk_to_client : boost::enable_shared_from_this<talk_to_client>
       {
    
           boost::recursive_mutex cs;
           boost::recursive_mutex cs_ask;
           bool in_process;
           void answer_to_client() {
    
               { boost::recursive_mutex::scoped_lock lk(cs_ask);
                 if ( in_process)
    
                     return;
                 in_process = true;
    
               }
               { boost::recursive_mutex::scoped_lock lk(cs);
               try {
    
                   read_request();
                   process_request();}
     catch ( boost::system::system_error&) {
    
                   stop();
    
               }
               }
               { boost::recursive_mutex::scoped_lock lk(cs_ask);
    
                 in_process = false;
               }
    

    } };

当我们处理一个客户端的时候,它的in_process变量被设置成true,其他的线程就会忽略这个客户端。额外的福利就是handle_clients_thread()方法不需要做任何修改;你可以随心所欲地创建你想要数量的handle_clients_thread()方法。

时间: 2024-10-07 10:25:30

Boost.Asio c++ 网络编程翻译(22)的相关文章

Boost.Asio c++ 网络编程翻译(1)

第一次翻译,希望大家多多指正 实战出精华 Boost.Asio C++ 网络编程 用具体的C++网络编程例子来提升你的技能 John Torjan 用具体的C++网络编程例子来提升你的技能 Copyright ? 2013 Packt Publishing 版权所有,除了在鉴定文章或者评论中进行简单引用,如果没有经过出版者事先的书面授权,该书的任何部分都不能被转载.存储在检索系统中.或者以任何形式和方式传阅. 在这本书准备发行之前,我们已经尽我们最大的努力去保证书中信息的准确性.但是,这本书中包

Boost.Asio c++ 网络编程翻译(20)

异步服务端 这个图表是相当复杂的:从Boost.Asio出来你可以看到4个箭头指向on_accept,on_read,on_write和on_check_ping.着也就意味着你永远不知道哪个异步调用是下一个完成的调用,但是你可以确定的是它是这4个操作中的一个. 现在,我们是异步的了:我们可以继续保持单线程.接受客户端连接是最简单的部分,如下所示: ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(), 8001)

Boost.Asio c++ 网络编程翻译(30)[完结]

PS:至此终于完成了Boost.Asio C++ network programming一书的翻译,这是我人生第一本完整翻译的书,从开始的磕磕绊绊,到最后小有心得,我收获很多.我将把这个系列的博客进行整理和校对,希望有兴趣的人可以帮我一起,来给大家提供更好更专业的阅读体验. 句柄追踪信息到文件 默认情况下,句柄的追踪信息被输出到标准错误流(相当于std::cerr).你想把输出重定向到其他地方的可能性是非常高的.对于控制台应用,输出和错误输出都被默认输出到相同的地方,也就是控制台.但是对于一个w

Boost.Asio c++ 网络编程翻译(14)

保持活动 假如,你需要做下面的操作: io_service service; ip::tcp::socket sock(service); char buff[512]; ... read(sock, buffer(buff)); 在这个例子中,sock和buff的存在时间都必须比read()调用的时间要长.也就是说,在调用read()返回之前,它们都必须有效.这就是你期望的:你传给一个方法的所有参数在参数内部都必须有效.当我们采用异步方式时,事情会变得越复杂. io_service servi

Boost.Asio c++ 网络编程翻译(3)

Boost.Asio入门 什么是Boost.Asio 简单来说,Boost.Asio是一个跨平台的.主要用于网络和其他一些底层输入/输出编程的C++库. 计算机网络的设计方式有很多种,但是Boost.Asio的的方式远远优于它们.它在2005年就被包含进Boost,然后被广大Bosot的用户测试并在很多项目中使用,比如Remobo(http://www.remobo.com),可以让你创建你自己的即时私有网络(IPN),libtorrent(http://www.rasterbar.com/pr

Boost.Asio c++ 网络编程翻译(11)

*_at方法 这些方法在一个流上面做随机存取操作.你来指定read和write操作从什么地方開始(offset): async_read_at(stream, offset, buffer [, completion], handler):这种方法在一个指定的流上从offset处開始运行一个异步的read操作,当操作结束时,他会调用handler. handler的格式为:void handler(const boost::system::error_code&  err, size_t byt

Boost.Asio c++ 网络编程翻译(26)

Boost.Asio-其他特性 这章我们讲了解一些Boost.Asio不那么为人所知的特性.标准的stream和streambuf对象有时候会更难用一些,但正如你所见.它们也有它们的益处.最后,你会看到姗姗来迟的Boost.Asio协程的入口,它能够让你的异步代码变的很易读.这是很惊人的一个特性. 标准stream和标准I/O buffer 读这一章节之前你须要对STL stream和STL streambuf对象有所了解. Boost.Asio在处理I/O操作时支持两种类型的buffer: b

Boost.Asio c++ 网络编程翻译(6)

io_service类 你应该已经发现大部分使用Boost.Asio编写的代码都会使用几个ios_service的实例.ios_service是这个库里面最重要的类:它负责和操作系统打交道,等待所有异步操作的结束,然后为每一个异步操作调用完成处理程序. 如果你选择用同步的方式来创建你的应用,你不需要考虑我将在这一节向你展示的东西. 你可以用几种不同的方式来使用io_service.在下面的例子中,我们有3个异步操作,2个socket连接和一个计时器等待: 有一个io_service和一个处理线程

Boost.Asio c++ 网络编程翻译(7)

Boost.Asio基本原理 这一章涵盖了你使用Boost.Asio时必须知道的一些事情.我们也将深入研究比同步编程更机警.更有乐趣的异步编程. 网络API 这一部分包含了当使用Boost.Asio编写网络应用程序时你必须知道的事情. Boost.Asio命名空间 Boost.Asio的一切都需要包含在boost::asio的命名空间或者其子命名空间内. boost::asio:这是核心类和函数所在的地方.重要的类有io_service和streambuf.类似read, read_at, re

Boost.Asio c++ 网络编程翻译(4)

同步VS异步 首先,异步编程和同步编程是有极大的不同的.在同步编程中,你所有的操作都是顺序执行的,比如从一个socket中读取(请求),然后写入(回应)到socket中.每一个操作操作都是阻塞的.因为操作是阻塞的,所以为了不影响主程序,当读写一个socket时,通常创建一个或多个线程来处理socket的输入/输出.因此,同步的服务端/客户端通常是多线程的. 相反的,异步编程是事件驱动的.你启动了一个操作,但是你不知道它何时会结束:你只是提供一个回调,当操作结束时,它会调用这个API,并返回操作结