同步服务端

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

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

正常来说服务端都比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())

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’之后的消息被保存到缓冲区中)。然后我们解析读取到的整个消息。剩下的代码都是易懂的。








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

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

    异步服务端 这个图表是相当复杂的:从Boost.Asio出来你能够看到4个箭头指向on_accept.on_read,on_write和on_check_ping. 着也就意味着你永远不知道哪个异步调 ...

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

    保持活动 假如,你须要做以下的操作: io_service service; ip::tcp::socket sock(service); char buff[512]; ... read(sock, ...

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

    Boost.Asio-其他特性 这章我们讲了解一些Boost.Asio不那么为人所知的特性.标准的stream和streambuf对象有时候会更难用一些,但正如你所见.它们也有它们的益处.最后,你会看 ...

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

    *_at方法 这些方法在一个流上面做随机存取操作.你来指定read和write操作从什么地方開始(offset): async_read_at(stream, offset, buffer [, co ...

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

    同步VS异步 Boost.Asio的作者做了一个非常惊艳的工作:它能够让你在同步和异步中自由选择,从而更好的适应你的应用. 在之前的章节中,我们学习了每种类型应用的框架,比方同步client,同步服务 ...

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

    TCP异步服务端 核心功能和同步服务端的功能类似,例如以下: class talk_to_client : public boost::enable_shared_from_this<talk_ ...

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

    read/write方法 这些方法对一个流进行读写操作(能够是套接字,或者其它表现的像流的类): async_read(stream, buffer [, completion],handler):这 ...

  8. boost.asio系列——socket编程

    asio的主要用途还是用于socket编程,本文就以一个tcp的daytimer服务为例简单的演示一下如何实现同步和异步的tcp socket编程. 客户端 客户端的代码如下: #include &l ...

  9. 使用boost.asio实现网络通讯

    #include <boost/asio.hpp> #define USING_SSL //是否加密 #ifdef USING_SSL #include <boost/asio/ss ...

随机推荐

  1. LA 7049 Galaxy 枚举

    题意: \(x\)轴上有\(n\)个质量为\(1\)的点,他们的坐标分别为\(x_i\). 质心的坐标为\(\frac{\sum{x_i}} {n}\) 转动惯量为\(\sum{d_i^2}\),其中 ...

  2. python基础学习笔记——网络编程(协议篇)

    一 互联网的本质 咱们先不说互联网是如何通信的(发送数据,文件等),先用一个经典的例子,给大家说明什么是互联网通信. 现在追溯到八九十年代,当时电话刚刚兴起,还没有手机的概念,只是有线电话,那么此时你 ...

  3. springboot-vue-前后端数据交互

    前端项目: pom文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&quo ...

  4. poj2449 Remmarguts' Date K短路 A*

    K短路裸题. #include <algorithm> #include <iostream> #include <cstring> #include <cs ...

  5. python中os和sys模块

    os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境. os 常用方法 os.rem ...

  6. K-means算法的优缺点

    K-means算法的优缺点 优点:原理简单,实现容易 缺点: 收敛较慢 算法时间复杂度比较高 \(O(nkt)\) 不能发现非凸形状的簇 需要事先确定超参数K 对噪声和离群点敏感 结果不一定是全局最优 ...

  7. Django多变关联、增加数据、删除数据

    建立表之间的关联关系: models.py里面对表的字段及外键关系的设置如下: from django.db import models # Create your models here. #出版社 ...

  8. Educational Codeforces Round 38 (Rated for Div. 2)

    这场打了小号 A. Word Correction time limit per test 1 second memory limit per test 256 megabytes input sta ...

  9. iOS----精品开源库-开发强力助攻

    30个精品iOS开源库,超强助攻 你不会想错过他们,真的. 我爱开源.  文章的尾部你会看到一个太长不看的版本——一个简单的列表,只有标题和到项目的链接.如果你发现这篇文章是有用的,把它和你的iOS开 ...

  10. bzoj1023【SHOI2008】cactus仙人掌图

    题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1023 求一棵仙人掌的直径 sol :orz YDC神犇 http://ydcydcy1.blo ...