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

同步服务端

同步服务端也相当简单。它需要两个线程,一个负责接收新的客户端,另外一个负责处理已经存在的客户端。它不能使用单线程;等带一个新的客户端是一个阻塞操作,所以我们需要另外一个线程来处理已经存在的客户端。

正常来说服务端都比客户端要难实现。一方面,它要管理所有已经连接的客户端。因为我们是同步的,所以我们需要至少两个线程,一个接受新的客户端连接(因为accept()是阻塞的)而另一个负责回复已经存在的客户端。

   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_);

} }

   void handle_clients_thread() {
       while ( true) {
           boost::this_thread::sleep( millisec(1));
           boost::recursive_mutex::scoped_lock lk(cs);
           for(array::iterator b = clients.begin(),e = clients.end(); b

!= e; ++b)

               (*b)->answer_to_client();
           // 删除已经超时的客户端
           clients.erase(std::remove_if(clients.begin(), clients.end(),
                      boost::bind(&talk_to_client::timed_out,_1)),
   clients.end());
       }
   }
   int main(int argc, char* argv[]) {
       boost::thread_group threads;
       threads.create_thread(accept_thread);
       threads.create_thread(handle_clients_thread);
       threads.join_all();

}

为了分别处理客户端发送过来的请求我们需要保存一个客户端的列表。

每个talk_to_client实例都拥有一个socket,socket类是不支持拷贝构造的,所以如果你想要把它们保存在一个std::vector方法中,你需要一个指向它的智能指针。这里有两种实现的方式:在talk_to_client内部保存一个指向socket的智能指针然后创建一个talk_to_client实例的数组,或者让talk_to_client实例用变量的方式保存socket,然后创建一个指向talk_to_client智能指针的数组。我选择后者,但是你也可以选前面的方式:

typedef boost::shared_ptr<talk_to_client> client_ptr;
   typedef std::vector<client_ptr> array;
   array clients;
   boost::recursive_mutex cs; // 用线程安全的方式访问客户端数组

talk_to_client的主要代码如下:

struct talk_to_client : boost::enable_shared_from_this<talk_to_client>
   {
       talk_to_client() { ... }
       std::string username() const { return username_; }
       void answer_to_client() {
           try {
               read_request();
               process_request();
           } catch ( boost::system::system_error&) {

stop(); }

           if ( timed_out())
               stop();
       }
       void set_clients_changed() { clients_changed_ = true; }
       ip::tcp::socket & sock() { return sock_; }
       bool timed_out() const {
           ptime now = microsec_clock::local_time();
           long long ms = (now - last_ping).total_milliseconds();
           return ms > 5000 ;
       }
       void stop() {
           boost::system::error_code err; sock_.close(err);
       }
void read_request() {
           if ( sock_.available())

read_)); }

... private:

already_read_ += sock_.read_some(
    buffer(buff_ + already_read_, max_msg - already_
       // ...  same as in Synchronous Client
       bool clients_changed_;
       ptime last_ping;

};

上述代码拥有非常好的自释。最重要的方法是read_request()。它只有在存在有效数据的情况才读取,这样的话,服务端永远不会阻塞:

void process_request() {
       bool found_enter = std::find(buff_, buff_ + already_read_, ‘\n‘)
                           < buff_ + already_read_;
       if ( !found_enter)
           return; // message is not full
       // process the msg
       last_ping = microsec_clock::local_time();
       size_t pos = std::find(buff_, buff_ + already_read_, ‘\n‘) -
   buff_;
       std::string msg(buff_, pos);
       std::copy(buff_ + already_read_, buff_ + max_msg, buff_);
       already_read_ -= pos + 1;
       if ( msg.find("login ") == 0) on_login(msg);
       else if ( msg.find("ping") == 0) on_ping();
       else if ( msg.find("ask_clients") == 0) on_clients();
       else std::cerr << "invalid msg " << msg << std::endl;
   }
   void on_login(const std::string & msg) {
       std::istringstream in(msg);
       in >> username_ >> username_;
       write("login ok\n");
       update_clients_changed();

}

void on_ping() {

       write(clients_changed_ ? "ping client_list_changed\n" : "ping
   ok\n");
       clients_changed_ = false;
   }
   void on_clients() {
       std::string msg;
       { boost::recursive_mutex::scoped_lock lk(cs);
           for( array::const_iterator b = clients.begin(), e = clients.
   end() ;
                b != e; ++b)
           msg += (*b)->username() + " ";

}

       write("clients " + msg + "\n");
   }
   void write(const std::string & msg) { sock_.write_some(buffer(msg)); }

观察process_request()。当我们读取到足够多有效的数据时,我们需要知道我们是否已经读取到整个消息(如果found_enter为真)。这样做的话,我们可以使我们避免一次读多个消息的可能(’\n’之后的消息被保存到缓冲区中),然后我们解析读取到的整个消息。剩下的代码都是易懂的。

时间: 2024-10-02 02:04:28

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

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,并返回操作结