zoukankan      html  css  js  c++  java
  • Boost.Asio c++ 网络编程翻译(18)

    同步服务端
    同步服务端也相当简单。它须要两个线程,一个负责接收新的client。另外一个负责处理已经存在的client。

    它不能使用单线程;等带一个新的client是一个堵塞操作,所以我们须要另外一个线程来处理已经存在的client。

    page84image4280
    正常来说服务端都比client要难实现。一方面,它要管理全部已经连接的client。由于我们是同步的,所以我们须要至少两个线程。一个接受新的client连接(由于accept()是堵塞的)而还有一个负责回复已经存在的client。
       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();
    

    为了分别处理client发送过来的请求我们须要保存一个client的列表。
    每一个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())
    
    page87image1520

    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_, '
    ')
    
                               < 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_, '
    ') -
    
       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
    ");
           update_clients_changed();
    

    void on_ping() {

           write(clients_changed_ ? "ping client_list_changed
    " : "ping
    
       ok
    ");
           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 + "
    ");
       }
    
       void write(const std::string & msg) { sock_.write_some(buffer(msg)); }
    
    观察process_request()。当我们读取到足够多有效的数据时,我们须要知道我们是否已经读取到整个消息(假设found_enter为真)。这样做的话。我们能够使我们避免一次读多个消息的可能(’ ’之后的消息被保存到缓冲区中)。然后我们解析读取到的整个消息。剩下的代码都是易懂的。








  • 相关阅读:
    Confluence 6 从一个备份中获得文件附件
    Confluence 6 从其他备份中恢复数据
    Confluence 6 从生产环境中恢复一个测试实例
    从 Confluence 5.3 及其早期版本中恢复空间
    Confluence 6 恢复一个空间的问题解决
    Confluence 6 从一个 XML 备份中导入一个空间
    Confluence 6 恢复一个空间
    Confluence 6 恢复一个站点有关使用站点导出为备份的说明
    网易蜂巢(云计算基础服务)MongoDB服务重磅来袭
    pdfjs viewer 开发小结
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7221179.html
Copyright © 2011-2022 走看看